Search code examples
iphonecocoa-touchuikituitabbar

How to recreate the UITabBarItem image filter?


I'm writing a custom UITabBar replacement, and I would like to know how to recreate the filter that the built-in implementation does with the UITabBarItem image - that blue shining on selected tabs and gray gradient on unselected ones. I guess it's a matter of using the source image alpha value as a mask and overlay it with a pre-made blue (or whatever color) shining image and another one grayed out, but I would like to know what is the best approach from a code point of view.

Best,


Solution

  • Edit: Fixed up the blue filter a little
    Edit2: Cleaned up the grey filter

    I required code to do these effects, so I wrote a couple functions for them:

    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;
    }
    

    To make the blue filter effect work you'll need to include this image in your project as "selection_gradient.png": selection_gradient.png
    Also, you may want to play with the defines to get the effect exactly how you like, I didn't take much time to perfect them, though they look good enough to me.

    Of course I don't know the exact filters that Apple applied, but I "guestimated" them and they look alright to me. I'm not sure if these functions are iPhone 4 compatible because I'm only using them in an iPad app, but it wouldn't be hard to edit them to your liking.