如何重新创建UITabBarItem图像过滤器?

4
我正在编写一个自定义UITabBar替代品,我想知道如何重新创建内置实现与UITabBarItem图像过滤器相同的效果-即所选标签上的蓝色闪光和未选中标签上的灰色渐变。我猜这涉及使用源图像alpha值作为掩码,并将其叠加在预制的蓝色(或任何颜色)发光图像和另一个灰色的图像上,但我希望从代码方面了解最佳方法。
此致,
最好的。

你可以为每个选项卡使用两张不同的图片(一张灰色,一张高亮),并在单击选项卡按钮时在它们之间切换...(当然,这仅适用于您为您的应用程序创建此自定义选项卡栏替代品,而不是作为可重用组件) - Swapnil Luktuke
嗨lukya,感谢您的反馈。对于每个项目使用两个图像是显而易见的解决方案,但我想通过编码来实现它,以使其可重复使用,就像您所说的那样。 - boliva
3个回答

11

编辑:稍微改进了蓝色滤镜
编辑2:清理了灰色滤镜

我需要实现这些效果的代码,所以我写了几个函数:

UIImage *grayTabBarItemFilter(UIImage *image) {
    int width = image.size.width, height = image.size.height;
    UIImage *result = image;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL) {
        return result;
    }
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
    if (context == NULL) {
        CGColorSpaceRelease(colorSpace);
        return result;
    }
    CGFloat colors[8] = {80/255.0,80/255.0,80/255.0,1, 175/255.0,175/255.0,175/255.0,1};
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
    CGContextDrawLinearGradient(context, gradient, CGPointMake(0,-(32-height)/2.0), CGPointMake(0,height+(32-height)/2.0), 0);
    CGGradientRelease(gradient);
    CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
    CGContextDrawImage(context, CGRectMake(0,0,width,height), image.CGImage);
    CGImageRef newImage = CGBitmapContextCreateImage(context);
    if (newImage != NULL) {
        result = [UIImage imageWithCGImage:newImage];
        CGImageRelease(newImage);
    }
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    return result;
}

struct RGBA {
    unsigned char red;
    unsigned char green;
    unsigned char blue;
    unsigned char alpha;
};

#define BLUE_ALPHA_THRESHOLD 128
#define BLUE_BRIGHTNESS_ADJUST 30

UIImage *blueTabBarItemFilter(UIImage *image) {
    int width = image.size.width,
        height = image.size.height;
    UIImage *result = image;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL) {
        return result;
    }
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
    if (context == NULL) {
        CGColorSpaceRelease(colorSpace);
        return result;
    }
    UIImage *gradient = [UIImage imageNamed:@"selection_gradient.png"];
    CGContextDrawImage(context, CGRectMake(-(gradient.size.width - width) / 2.0, -(gradient.size.height - height) / 2.0, gradient.size.width, gradient.size.height), gradient.CGImage);
    CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
    CGContextDrawImage(context, CGRectMake(0,0,width,height), image.CGImage);
    struct RGBA *pixels = CGBitmapContextGetData(context);
    if (pixels != NULL) {
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int offset = x+y*width;
                if (pixels[offset].alpha >= BLUE_ALPHA_THRESHOLD && 
                    ((x == 0 || x == width-1 || y == 0 || y == height-1) ||
                     (pixels[x+(y-1)*width].alpha < BLUE_ALPHA_THRESHOLD) ||
                     (pixels[x+1+y*width].alpha < BLUE_ALPHA_THRESHOLD) ||
                     (pixels[x+(y+1)*width].alpha < BLUE_ALPHA_THRESHOLD) ||
                     (pixels[x-1+y*width].alpha < BLUE_ALPHA_THRESHOLD))) {
                    pixels[offset].red = MIN(pixels[offset].red + BLUE_BRIGHTNESS_ADJUST,255);
                    pixels[offset].green = MIN(pixels[offset].green + BLUE_BRIGHTNESS_ADJUST,255);
                    pixels[offset].blue = MIN(pixels[offset].blue + BLUE_BRIGHTNESS_ADJUST,255);
                }
            }
        }
        CGImageRef image = CGBitmapContextCreateImage(context);
        if (image != NULL) {
            result = [UIImage imageWithCGImage:image];
            CGImageRelease(image);
        }
    }
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    return result;
}
为了让蓝色滤镜效果生效,您需要将名称为"selection_gradient.png"的图像包含在您的项目中:selection_gradient.png
此外,您可能想要尝试更改宏定义,以便获得满意的效果。虽然我没有花太多时间来完善它们,但是它们看起来足够好。
当然,我不知道苹果应用的确切滤镜,但我进行了一些估算并且它们看起来还可以接受。我不确定这些函数是否适用于iPhone 4,因为我只在iPad应用程序中使用它们,但将其编辑为符合您要求的形式并不难。

我在尝试在视网膜设备上使用此过滤器时遇到了问题。有人成功做到了吗?我已经尝试过CGContextScaleCTM(),但那只会使图像变大两倍。 - eric.mitchell
你只需将result = [UIImage imageWithCGImage:newImage];更改为result = [UIImage imageWithCGImage:newImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];即可。 - poiuy_qwert
我实际上找到了一种方法来精确复制UITabBarItem过滤器。这有点像是一个hack,但它是一个完美的像素复制。我马上会发布我的解决方案。 - eric.mitchell
刚刚发布了我的答案。提供蓝色和灰色滤镜。 - eric.mitchell

1

试试这个,它更短:

+ (UIImage *)blendImageBlue:(UIImage *)senderImage {
        UIImage *image = [UIImage imageNamed:@"selection_gradient"];

        CGSize newSize = CGSizeMake(senderImage.size.width, senderImage.size.height);
        UIGraphicsBeginImageContextWithOptions(newSize, NO, [UIScreen mainScreen].scale);

        [senderImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
        [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height) blendMode:kCGBlendModeSourceAtop alpha:0.8];

        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        return newImage;
}

编辑:使用 @poiuy_qwert 的 "selection_gradient.png"


我还没有测试你的代码,但从我的理解来看,这并没有使图标的边缘更亮? - poiuy_qwert

0
如果您对UITabBarItem筛选器的精确复制感兴趣,可以尝试这个解决方案。您不需要将任何额外的图像包含到您的项目中。
我完全意识到这是一个彻头彻尾的黑客行为,并且不能保证在未来兼容性,但据我所知它可以在iOS 5和iOS 6上工作,并且通过适当的错误处理,我认为它可以很有用。下面是具体步骤:
UIImage *grayTabBarItemFilter(UIImage *image) {
    UITabBar* bar = [[UITabBar alloc] init];
    UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"" image:image tag:0];
    [bar setItems:@[item]];
    [[[[UIApplication sharedApplication] windows] lastObject] addSubview:bar];
    UIImage* returnImage;
    for(UIView* view in bar.subviews) {
        for(UIView* small in view.subviews) {
            if([small respondsToSelector:@selector(image)]) {
                returnImage = [(UIImageView*)small image];
            }
        }
    }

    [bar removeFromSuperview];

    return returnImage ? returnImage : image;
}

UIImage *blueTabBarItemFilter(UIImage *image) {
    UITabBar* bar = [[UITabBar alloc] init];
    UITabBarItem* item = [[UITabBarItem alloc] initWithTitle:@"" image:image tag:0];
    [bar setItems:@[item]];
    [bar setSelectedItem:item];
    [[[[UIApplication sharedApplication] windows] lastObject] addSubview:bar];
    UIImage* returnImage;
    for(UIView* view in bar.subviews) {
        NSInteger count = 0;
        for(UIView* small in view.subviews) {
            if([small respondsToSelector:@selector(image)]) {
                count++;
                if(count > 1) {
                    returnImage = [(UIImageView*)small image];
                }
            }
        }
    }

    [bar removeFromSuperview];

    return returnImage ? returnImage : image;
}

我知道这只是一个不太稳定的解决方案,但如果你对完美的复制品感兴趣,这里就有了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接