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.
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:
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.
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?
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?
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.
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.