Thursday, June 07, 2012

Change iOS Navigation App to Tab and Nav App

I have an app with UINavigationController as start view. Later on, I wanted to to change the start view with both navigation bar on the top and a group of tab bars on the bottom. There are many apps like this style, however, there is no template for this type of app in XCODE.

This is a frequently asked question on web for iOS development. There are some information available, but most of them are iOS 4.0 or early with xib based interface. For iOS 5.0+, Apple introduced storyboard. I struggled for a while, but finally I figured it out. The fact is that it much easier in iOS storyboard than the previous IB(Interface builder).  Here is the summary of what did.

Add Tab Bar Controller as Start View

My app was designed with XCODE Navigation template.  In the storyboard, the first view of my app's workflow is a Navigation Controller:

I want to change my app's first view with both navigation on top and tab bars on bottom respectively. The first thing I changed was to add a Tab Bar Controller, which is available from storyboard's library (inspector view on the bottom in XCODE). Just drag and drop the Tab bar controller to my storyboard.  Here is what Tab Bar Controller looks like in the storyboard.

Notice that there are three view items being placed in.  The first one is the root or master view: Tab Bar Controller, and other two are normal View Controllers, each with one Tab Bar Item on the bottom in its view. For the later two view controller items, the image and text can be changed from inspector. In addition to views, there are two segues, relationship segue type, being used to link the master Tab Bar Controller to other tab items.

Now it is time to make changes. First, I need to change the master, Tab Bar Controller, to my app's start point. This can be done by checking inspector's property: Is initial view Controller:

Notice that an arrow is added to the Tab Bar Controller on the left.

Then, control+drag Tab Bar Controller on the bottom of the view (right icon) to an existing Navigation Controller. A pop up menu is displayed.  Choose Relationship segue from the pop up menu. That's it! The app is changed to an app with both navigation and tab bars in the first view.

To change the position of tabs, just drag a tab icon and move it around.

Based on this practice, it is very easy to add more view controllers to the tab bar controller: use the relationship segue to link the tab bar controller and other view controllers. All those can be done by just dragging and dropping.

Add Navigation Controllers

To add navigation controller to the start view, simple drag and drop relationship segues so that the existing or the newly added navigation controllers as the next controller after tab bar controller, before other view controllers

Two controllers in a sequence in this way, as a result, will make the start view showing both navigation bar on the top and tab bars on the bottom.

iOS' storyboard makes the change so easy. I really like the improvement of iOS 5. The following sections are additional information about those controllers and my experience of using them.

iOS Controllers: Tab Bar and Navigation

It is important to know that both Tab Bar Controller and Navigation Controllers are only a view component. They are not View Controllers. In other words, they are part of view or window on iPhone or iPad.

Initially, I tried to create customized class based on tab bar controller with navigation delegate.  However, I could not associate my customized class with view controllers. Soon I realized that my customized class is NOT VIEW CONTROLER. In storyboard, the class for Tab Bar Controller or Navigation Controller is Controller, but not View Controller (for example, UITabBarController and UITableViewController).

Both controllers are visible in view controller class, where they are part of view and they direct view flow. For example, the following codes are some examples in a view controller to access to either Tab Bar Controller or Navigation Controller, as well as navigation item.

NSInteger row = [self.tabBarController selectedIndex];
[self.navigationController popToRootViewControllerAnimated:YES];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.navigationItem.leftBarButtonItem = nil;

Access to Tab Bar Controller and Tab Bar Item

If you set view's title with a string, the tab bar item's title will be changed, as same as the view's title. Normally, I prefer the tab bar item's title shorter than view's title since tab bat item has limited space. Here is a tip to set tab bar item's tile with a short string and set the view's title with a long string:

self.navigationItem.title = @"Edit entities";
self.navigationController.tabBarItem.title = @"Edit";

Within the storyboard, a tab bar item has a tag property.  This tag's data type is integer. Many developers use the tag value as an identifier for a tab bar.

One thing I realize is that a tab bar item cannot be set as an outlet in a view controller class. The reason is very simple, the "view" class that the tab bar item resides is a controller but view controller. Therefore, you cannot control-drag a tab bar to a view controller as an outlet.

However, it is possible to define a customized class based on UITabBarController and then control+dragging the tab bar item to the customized class as an outlet.  Here is an example I tried:

// my customized class for UITabBarController
@interface NavigationWithTabBarController : UINavigationController

@property (weak, nonatomic) IBOutlet UITabBarItem *myTabBarItem;


// In my view controller .m class
UITabBarItem* tb = [self.navigationController

if (tb.tag == 1) ...

Later, I found out that actually, I don't need to define my customized class at all. The tab bar item is accessible from view controller's navigation controller:

// In my view controller .m class
UITabBarItem* tb = self.navigationController.tabBarItem;

if (tb.tag == 1) ...

Even though, I make this note on this experience. There may be cases where a customized class is needed.

Another tip is about disabling tab bars or making tab bar controller on the bottom invisible. With both tab and navigation bars as start view, the tab bar will  always be displayed along navigation. In some cases, you may want to make tab bars invisible. To do that, just  overwrite the following method in your view controller class like this, making tab bars invisible:

#pragma mark - UINavigationController methods overwrites

- (BOOL) hidesBottomBarWhenPushed {
   return YES;

To disable tab bar items, for example, to prevent switching to other tabs while editing table view cells, use the following method:

- (void) setTabBarItemsWhileEditing:(BOOL)editing {
   for (UITabBarItem* tabBarItem in [self.tabBarController.tabBar items]) {
       [tabBarItem setEnabled:!editing];

This method is also used to enable them when not in editing mode.