views:

5325

answers:

6

When I make icons for a UITabBar, it applies a gradient to the images. I need to know how to prevent it from having this gradient.

+10  A: 

This is surprisingly difficult as the UITabBar doesn't provide access to it's selected/unselected images. It can be achieved with a private API though:

@interface UITabBar (ColorExtensions)
- (void)recolorItemsWithColor:(UIColor *)color shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset shadowBlur:(CGFloat)shadowBlur;
@end

@interface UITabBarItem (Private)
@property(retain, nonatomic) UIImage *selectedImage;
- (void)_updateView;
@end

@implementation UITabBar (ColorExtensions)
- (void)recolorItemsWithColor:(UIColor *)color shadowColor:(UIColor *)shadowColor shadowOffset:(CGSize)shadowOffset shadowBlur:(CGFloat)shadowBlur
{
    CGColorRef cgColor = [color CGColor];
    CGColorRef cgShadowColor = [shadowColor CGColor];
    for (UITabBarItem *item in [self items])
        if ([item respondsToSelector:@selector(selectedImage)] &&
            [item respondsToSelector:@selector(setSelectedImage:)] &&
            [item respondsToSelector:@selector(_updateView)])
        {
            CGRect contextRect;
            contextRect.origin.x = 0.0f;
            contextRect.origin.y = 0.0f;
            contextRect.size = [[item selectedImage] size];
            // Retrieve source image and begin image context
            UIImage *itemImage = [item image];
            CGSize itemImageSize = [itemImage size];
            CGPoint itemImagePosition; 
            itemImagePosition.x = ceilf((contextRect.size.width - itemImageSize.width) / 2);
            itemImagePosition.y = ceilf((contextRect.size.height - itemImageSize.height) / 2);
            UIGraphicsBeginImageContext(contextRect.size);
            CGContextRef c = UIGraphicsGetCurrentContext();
            // Setup shadow
            CGContextSetShadowWithColor(c, shadowOffset, shadowBlur, cgShadowColor);
            // Setup transparency layer and clip to mask
            CGContextBeginTransparencyLayer(c, NULL);
            CGContextScaleCTM(c, 1.0, -1.0);
            CGContextClipToMask(c, CGRectMake(itemImagePosition.x, -itemImagePosition.y, itemImageSize.width, -itemImageSize.height), [itemImage CGImage]);
            // Fill and end the transparency layer
            CGContextSetFillColorWithColor(c, cgColor);
            contextRect.size.height = -contextRect.size.height;
            CGContextFillRect(c, contextRect);
            CGContextEndTransparencyLayer(c);
            // Set selected image and end context
            [item setSelectedImage:UIGraphicsGetImageFromCurrentImageContext()];
            UIGraphicsEndImageContext();
            // Update the view
            [item _updateView];
        }
}
@end

One can even create some pretty cool effects:

Red Tab Bar

It is very possible that Apple will reject an application for doing this. If the private API is removed in a future OS update,
-[UITabBar recolorItemsWithColor:shadowColor:shadowOffset:shadowBlur:] will do nothing instead of crashing.

rpetrich
This was a good suggestion, but I chose a less complicated approach. I subclassed UIToolBar, which allowed it to adapt the functionality of a UITabBar without its control over the look and feel (UIToolBar supports custom views).
obsoleteModel81
Can anybody show me how I can implement this code in a new "Tab Bar application" project?
mofle
mofie: Add the code above to your appdelegate, then add this call to applicationDidFinishLaunching: [[tabBarController tabBar] recolorItemsWithColor:[UIColor whiteColor] shadowColor:[UIColor blackColor] shadowOffset:CGSizeMake(0.0f, -1.0f) shadowBlur:3.0f];
rpetrich
Was It approved by Apple?
mxg
I haven't used it in an app store app (I'm a jailbreak developer)
rpetrich
how did you get the gradient in icons? When I set a color it I just plain.
mofle
Use +[UIColor colorWithPatternImage:]
rpetrich
My app got rejected because of "_updateView". Is there a way to do this without it?
mofle
There are, but that's venturing further into "private API" territory
rpetrich
How would I use "[UIColor colorWithPatternImage:]" to get the blue gloss effect like the original, only with a different color?
mofle
-1: not a good answer since it uses a private API.
jkp
@jkp: clearly labeled as such. people can make their own decisions. not everyone develops for the App Store
rpetrich
A: 

Thank you very much for this post, but I have one other question. How did you get the nice gradient effect in the image? Did you use a modified version of the above code?

thamer
A: 

I have tried this but not getting this type of effect i can not able to change the color from blue to red. Can somebody help me?

dipen
+2  A: 

Adding gradient is very simple, add the following lines of code:


CGFloat components[8] = {0.0,0.4,1.0,0.2,0.0,0.6,1.0,1.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();  
CGGradientRef colorGradient = CGGradientCreateWithColorComponents(colorSpace, components, NULL, 2);
CGContextDrawLinearGradient(cxt, colorGradient,CGPointZero,CGPointMake(0,contextRect.size.height),0);

This will get you very close to what Apple does on the tabbar but, not exact. To get there, just add two more points/colors in components and instead of NULL in CGGradientCreateWithColorComponents, use something like {0,0.5,0.6,1.0}. In fact, all you need is one background color and three alpha points (with color part being all 1s, since you just need shading while retaining a single color profile).

I will post my code if this isn't clear ... cheers.

Genzeb
A: 

Can you please post your code? I tried adding this gradient you said but it only added a blue background to the whole UITabBarItem instead of just the icon. I am interested in creating that little shadowed triangle you can see on the tab bar selected icons when you don't change the color.

Roi
+2  A: 

Due to the use of the private API _updateView my app failed Apple's review. Is there any other way other than using this private API to change the tab bar item color?

Roi
Recreate a tab bar from scratch.
rpetrich