In a previous blogpost, which is still one of my most popular ones, I gave a method on how to create a tabBar with complete custom icons. It’s really more like a hack. But so far I haven’t found anything better yet. My method didn’t use any private API’s, and I have already succesfully submitted two apps in the App Store: Kust Afvalgids and Cultuurmarkt. The former one suffers from a little inconvenience: when you touch a given icon on your tabBar, you can still see the sort of “transparent” area covering the location of the icon, to indicate that this icon is active. You can get rid of that though. Here’s how.
A custom UITabBarItem implementation
To get rid of the annoying active state of an icon, all you have to do is implement your own class which inherits from UITabBarItem, and override the different icons. Then in the code you can create an instance of your custom UITabBarItem class, and assign it to the tabBarItem of your controller. Here’s how I implemented the subclass:
// CustomTabBarItem.h
@interface CustomTabBarItem : UITabBarItem {
UIImage *customHighlightedImage;
UIImage *customStdImage;
}
@property (nonatomic, retain) UIImage *customHighlightedImage;
@property (nonatomic, retain) UIImage *customStdImage;
@end
// CustomTabBarItem.m
#import "CustomTabBarItem.h"
@implementation CustomTabBarItem
@synthesize customHighlightedImage;
@synthesize customStdImage;
- (void) dealloc
{
[customHighlightedImage release]; customHighlightedImage=nil;
[customStdImage release]; customStdImage=nil;
[super dealloc];
}
-(UIImage *) selectedImage
{
//return self.customHighlightedImage;
return nil;
}
-(UIImage *) unselectedImage
{
//return self.customStdImage;
return nil;
}
@end
Two methods need to be overridden: selectedImage, which returns the icon you want to display in the active state; and unselectedImage, which returns the icon you want to display in the inactive state. As you can see in my code, I simply return nil. This indicates that I have no icons for both active & normal state. If you’ve read my previous blog post, you know I have just created different background images for the tabBar. Each background image already has the different images in it, so I don’t need separate icons for tabBarItems. If you do work like that, you just return the correct images in the correct methods (the parts which are commented out).
Now in each sub-controller of your tabBar, you make an instance of the CustomTabBarItem class, and assign it to self.tabBarItem. Example:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
//tabItem.customHighlightedImage=[UIImage imageNamed:@"icon_active.png"];
//tabItem.customStdImage=[UIImage imageNamed:@"icon_inactive.png"];
self.tabBarItem=tabItem;
[tabItem release];
tabItem=nil;
}
Again, I provided sample code to show you how to specify the different images. Each time your view will appear, your tabBarItem will be set to your custom implementation. There is still one (little) problem: If you tap a selected tabBarItem twice, the active state will show. If you have a view transition, like a flip view animation, the active state will show. I have not found a way around this yet. I guess it’s somewhere in the internals of cocoa and I have no clue how to solve it. If you do find a solution, please let me know via the comments.
Conclusion
In my latest iPhone app, I have used the system of my previous blogpost to display different background images for the tabBar per controller. Then I have combined this with a custom tabBarItem implementation to get rid of the highlight state by returning nil for selectedImage & unselectedImage. If you don’t mind that this isn’t totally bug free, then this method is a good solution I think. Apps get approved with this code, since you don’t call any private API methods.
Update – For those of you who want to see an example of the two methods combined, here’s a piece of code:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self setCustomTabBarItem];
for(UIView *view in self.tabBarController.tabBar.subviews) {
if([view isKindOfClass:[UIImageView class]]) {
[view removeFromSuperview];
}
}
UIImageView *imgView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"custom_tabbar_image1.png"]] autorelease];
imgView.frame = CGRectMake(imgView.frame.origin.x, imgView.frame.origin.y - 2.0, imgView.frame.size.width, imgView.frame.size.height);
[self.tabBarController.tabBar insertSubview:imgView atIndex:0];
}
- (void)setCustomTabBarItem
{
// Remove highlight from tabbar item:
CustomTabBarItem *tabItem = [[CustomTabBarItem alloc] initWithTitle:@"" image:nil tag:0];
self.tabBarItem=tabItem;
[tabItem release];
tabItem=nil;
}