Mobile application SDKs are supplied with various default controls to help expatiate and standardize common application functionality. This is great when the application design lends itself to using these controls out of the box. But when customization is required, this can lead to trying to twist those controls to your will and lead to ugly code.
I was recently tasked with creating an app where the tab bar had an image background and each tab is represented by two images for the default and selected state. Rather than try and bend the iOS UITabBarController, I decided to use it as a guideline for creating my own custom tab bar. The downside is that the tab bar controller must be created in code and outside of Interface Builder, but all sub-screens can still be designed using IB.
The tab bar is broken down into three pieces:
Tab Bar Item
- Wrapper class for a UIButton, since UIButton cannot be directly subclassed
- Handles the initial touch request and setting of the button’s state (default, selected)
- If the state is changed, the delegate will be informed
- Contains the background image and the set of Tab Bar Items
- As Tab Bar Items are added, they will be evenly sized to fill the space of the Tab Bar (This is an area that could be customized more to allow for a bigger middle button)
- Delegate for Tab Bar Item, so when the Tab Bar Item notifies being selected, the Tab Bar will set the previously selected tab to the correct state and inform its own delegate of the change
Tab Bar Controller
- Contains the View Controllers associated with the Tab Bar Items and acts as the main view
- Delegate for the Tab Bar, so when the Tab Bar notifies a change of tabs, the corresponding View Controller will be shown
From here, I will go over how to set up the tab bar in the application delegate. If you are interested in the inner workings of each piece, grab the code here and read over it.
@interface ExampleCustomTabBarAppDelegate : NSObject <UIApplicationDelegate, CustomTabBarControllerDelegate>
@property (nonatomic, retain) IBOutlet UIWindow* window;
@property (nonatomic, retain) CustomTabBarController* customTabBarController;
Here we simply make the application delegate implement the CustomTabBarControllerDelegate protocol and add a instance variable and property for our CustomTabBarController.
@synthesize window = _window;
@synthesize customTabBarController = _customTabBarController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
CGRect screenRect = CGRectMake(0, 0, 320, 460);
_customTabBarController = [[CustomTabBarController alloc] initWithFrameAndImage:screenRect backgroundImageName:@"tab_background"];
[_customTabBarController addTabBarItem:@"Example1ViewController" defaultImageName:@"tab_1" selectedImageName:@"tab_1_selected" isSelected:TRUE];
[_customTabBarController addTabBarItem:@"Example2ViewController" defaultImageName:@"tab_2" selectedImageName:@"tab_2_selected" isSelected:FALSE];
[_customTabBarController addTabBarItem:@"Example3ViewController" defaultImageName:@"tab_3" selectedImageName:@"tab_3_selected" isSelected:FALSE];
[_customTabBarController addTabBarItem:@"Example4ViewController" defaultImageName:@"tab_4" selectedImageName:@"tab_4_selected" isSelected:FALSE];
self.window.rootViewController = _customTabBarController;
#pragma mark - CustomTabBarControllerDelegate
- (void)tabBarController:(CustomTabBarController*)tabBarController didSelectViewController:(UIViewController*)viewContoller
- (BOOL)customTabBarController:(CustomTabBarController*)tabBarController shouldSelectViewController:(UIViewController*)viewContoller
In the implementation file for the application delegate, we initialize our CustomTabBarController with a frame and the name of the background image. Tabs are then added to the controller by providing the class name of the tab’s View Controller and image names for tab states. Finally the Tab Bar Controller is set as the Window’s Root Controller.
Additionally the CustomTabBarControllerDelegate methods are implemented. The first allows for the application to be notified when the tab is changed. The second method allows for the application to decide whether or not to allow the tab to be selected.
This is by no means as eloquent as the iOS standard tab bar, but the core functionality is present and allows for livelier application design. In writing this tutorial, I’ve already spotted several areas that could be made more robust, but this was not meant to be the end all replacement of the iOS supplied tab bar. It is meant to serve as a leaping off point and good practice to further understand the workings of iOS. Not bad for an afternoon!
You can find the same project here.