Tab Bar Controllers

You use tab bar controllers to organize your application into one or more distinct modes of operation. A tab bar controller manages a self-contained view hierarchy (known as a tab bar interface) whose contents are composed partly of views managed by the tab bar controller and partly of views managed by custom view controllers you provide.

This chapter provides an overview of how you configure and use tab bar controllers in your application. For information about ways in which you can combine tab bar controllers with other types of view controller objects, see Combined View Controller Interfaces.

The Tab Bar Interface

A tab bar interface is useful in situations where you want to provide different perspectives on the same set of data or in situations where you want to organize your application along functional lines. The key component of a tab bar interface is the presence of a tab bar view along the bottom of the screen. This view is used to initiate the navigation between your application’s different modes and can also convey information about the state of each mode.

The manager for a tab bar interface is a tab bar controller object. The tab bar controller creates and manages the tab bar view and also manages the custom view controllers that provide the content view for each mode. Each custom view controller is designated as the root view controller for one of the tabs in the tab bar view. When a tab is tapped by the user, the tab bar controller object selects the tab and displays the view associated with the corresponding root view controller initially.

Figure 4-1 shows the tab bar interface implemented by the Clock application. The tab bar controller has its own container view, which encompasses all of the other views, including the tab bar view. The custom content is provided by the root view controller of the selected tab.

Figure 4-1  The views of a tab bar interface

Although a tab bar view is normally a customizable object, when it is part of a tab bar interface, it must not be modified. In a tab bar interface, the tab bar view is considered to be part of a private view hierarchy that is owned by the tab bar controller object. If you do need to change the list of active tabs, you must always do so using the methods of the tab bar controller itself. For information on how to modify a tab bar interface at runtime, see Managing Tabs at Runtime.

The Objects of a Tab Bar Interface

A standard tab bar interface consists of the following objects:

Figure 4-2 shows the relationship of the tab bar controller to its associated view controllers. Each view controller in the tab bar controller’s viewControllers property is a root view controller for a corresponding tab in the tab bar.

Figure 4-2  A tab bar controller and its associated view controllers

The custom view controllers you provide are the most important elements of the tab bar interface. Each view controller defines the content that is displayed when the corresponding tab is selected. You can use custom view controllers that display a single view or you can use a navigation controller to allow for more complex navigation within the tab. You would not, however, install another tab bar controller in the tab.

If you add more than five items to the viewControllers property, the tab bar controller automatically inserts a special view controller (called the More view controller) to handle the display of the additional items. The More view controller provides a custom interface that lists the additional view controllers in a table, which can expand to accommodate any number of view controllers. The More view controller cannot be customized or selected and does not appear in any of the view controller lists managed by the tab bar controller. For the most part, it appears automatically when it is needed and is separate from your custom content. You can get a reference to it though by accessing the moreNavigationController property of UITabBarController.

Although the tab bar view is a key part of your tab bar interface, you do not modify that view directly. The tab bar controller object assembles the contents of the tab bar from the UITabBarItem objects provided by your custom view controllers. Figure 4-3 shows the relationship between the tab bar controller, view controllers, and the tab bar item objects in the iPod application. Because there are more view controllers than can be displayed at once, tab bar items from only the first four view controllers are displayed. The final tab bar item is provided by the More view controller.

Figure 4-3  Tab bar items of the iPod application

Because tab bar items are used to configure the tab bar, you must configure the tab bar item of each root view controller prior to displaying the tab bar interface. If you are using Interface Builder to assemble your interface, you can specify the title and image as described in Creating a Tab Bar Interface Using a Nib File. If you are creating your tab bar interface programmatically, you must create a new UITabBarItem object for each of your view controllers as described in Creating a Tab Bar Interface Programmatically.

A tab bar controller also supports an optional delegate object that can be used to respond to tab bar selections and customizations. For information about responding to delegate-related messages, see Managing Tabs at Runtime.

Creating a Tab Bar Interface

Before creating a tab bar interface, you need to decide how you intend to use it. Because it imposes an overarching organization on your data, there are only a handful of appropriate ways to use a tab bar interface:

Installing a tab bar interface in your application’s main window is by far the most common way to use it. In such a scenario, the tab bar interface provides the fundamental organizing principle for your application’s data, with each tab leading the user to a distinct part of the application. Because it provides access to your entire application, it must be the root part of your window.

Of course, it is still possible to present a tab bar controller modally if a very specific need makes doing so worthwhile. For example, you could present a tab bar controller modally in order to edit some complex data set that had several distinct sets of options. Because a modal view fills all or most of the screen (depending on the device), the presence of the tab bar would simply reflect the choices available for viewing or editing the modally presented data. Using a tab bar in this way should be avoided though if a simpler design approach is available.

Defining the Custom View Controllers for a Tab Bar Interface

Because each mode of a tab bar interface is separate from all other modes, the root view controller in each tab essentially defines the content for that tab. Thus, the view controller you choose for each tab should reflect the needs of that particular mode of operation. If you need to present a relatively rich set of data, you might install a navigation controller to manage the navigation through that data. If the data being presented is simpler, you could install a custom view controller with a single view.

Figure 4-4 shows several screens from the Clock application. The World Clock tab uses a navigation controller primarily so that it can present the buttons it needs to edit the list of clocks. The Stopwatch tab requires only a single screen for its entire interface and therefore uses a single view controller. The Timer tab uses a custom view controller for the main screen and presents an additional view controller modally when the When Timer Ends button is tapped.

Figure 4-4  Tabs of the Clock application

Because the tab bar controller handles all of the interactions associated with presenting the root view controllers, there is very little you have to do with regard to managing tabs or the view controllers in them. Once displayed, your custom view controllers should simply focus on presenting their content.

Creating a Tab Bar Interface Using a Nib File

The semantics for loading a tab bar controller from a nib file are slightly different from those for custom view controllers. With a custom view controller, you use a nib file to store the views associated with the view controller, but you typically create the view controller separately—either programmatically or by loading it from a different nib file. However, a tab bar controller always creates its view programmatically and so there is no view to put in a separate nib file. One consequence of this behavior is that a tab bar controller never manages a nib file—in other words, you never assign a tab bar controller to the File’s Owner placeholder. Instead, the only time you mix tab bar controllers and nib files is when the tab bar controller itself is stored in the nib file. For this reason, a tab bar controller is almost always a passenger in a nib file managed by some other object.

It makes the most sense to include tab bar controllers in your application’s main nib file. You do this when the tab bar controller itself provides the main view for your application’s window. Although you could also load a modally presented tab bar controller from your main nib file (or any other nib file), doing so is not optimal. In fact, it is usually easier to create the tab bar controller programmatically at the point of use.

Figure 4-5 shows the typical configuration of a tab bar controller and its related objects in an application’s main nib file. Each custom view controller represents the root view controller associated with a single tab. Because they are all custom view controllers (and not navigation controllers), each view controller has a reference to a separate nib file containing its view. Although you could include the custom views in the main nib file, doing so is not recommended. Storing the views in separate nib files gives the system the option of purging them from memory as needed.

Figure 4-5  Nib file containing a tab bar interface

If you are creating your Xcode project from scratch, you can use the Tab Bar Application template to create your project. The main nib file that comes with this project includes a tab bar controller object that is minimally configured for the project.

The following steps show you how to add a tab bar controller to your application’s main nib file from scratch. If you are starting with the Tab Bar Application template, you can simply skip the first two steps.

  1. Drag a tab bar controller object from the library to your Interface Builder document window.

    When you add a tab bar controller to a nib file, Interface Builder also adds a tab bar view, two root view controllers, and two tab bar items (one for each view controller). You can access some of these objects by selecting them in the tab bar controller edit surface. You can also select any of the objects from the document window when in outline or browser modes.

    If you created your project using the Tab Bar Application template, this step is already done for you.

  2. Save a reference to the tab bar controller using an outlet.

    In order to access the tab bar controller at runtime, you either need to use an outlet or you must explicitly retrieve the nib file’s top-level objects when you load the nib file. Using an outlet is generally much simpler. To add an outlet, add a variable to the declaration for your application delegate class that is similar to the following:

    @interface MyAppDelegate : NSObject <UIApplicationDelegate> {
       IBOutlet UITabBarController*  myTabBarController;
    }
    @end

    After adding the outlet definition, create a connection from that outlet to the tab bar controller object.

    If you created your project using the Tab Bar Application template, this step is already done for you.

  3. Add or delete view controllers to reflect the desired number of tabs for your interface.

    The number of view controllers embedded inside your tab bar controller object determines the number of tabs displayed by your tab bar interface. Your final tab bar controller should have at least two view controllers; otherwise, the need for a tab bar controller becomes questionable. From the Interface Builder library, you can drag a View Controller object (UIViewController), Navigation Controller object (UINavigationController), or Table View Controller object (UITableViewController) and associate it with a tab.

    To add a view controller, do one of the following:

    • Drag the appropriate object from the library to the tab bar in the edit surface.

    • Drag the object from the library to the tab bar controller in your Interface Builder document window. The window must be in outline mode.

    When adding navigation or table view controllers to your interface, you should either drag the appropriate object from the library or select the tab bar controller and configure your view controller types using the Attributes inspector. Both of these options add the correct type of view controller object to your nib file. You should never add navigation or table view controllers by dragging a generic View Controller object to the nib file and change its class name to the desired class type.

    To delete a view controller, select the view controller object in the edit surface or document window and press the Delete key.

  4. Arrange the view controllers in the order you want them to appear onscreen.

    You can rearrange view controllers (and the corresponding tabs) by dragging the tabs displayed on the tab bar controller edit surface or by dragging the view controllers in the Interface Builder document window (outline mode only). Although the edit surface shows all of the tabs, only five are displayed at runtime. If your tab bar controller contains six or more view controllers, only the first four are displayed in the tab bar initially. The last spot on the tab bar is reserved for the More view controller, which presents the remaining view controllers.

  5. Configure the root view controller for each tab.

    For each root view controller, you should configure the following attributes:

    • Set the class of any custom view controller objects using the Identity inspector. If the root view controller is a generic View Controller object or a Table View Controller object, you can change the class name to your custom subclass. If it is a Navigation Controller object, do not change the class name.

    • Provide a view for the view controller. The preferred way to do this is to configure the NIB Name attribute of the view controller with the name of the nib file containing the view. Although you can include the view in your main nib file if you want, doing so is not recommended.

    • As appropriate, configure any style or appearance information for the view controller.

    If you are using a navigation controller for one of the root view controllers, configure it as described in Loading Your Navigation Interface from a Nib File. For additional information and examples of how to embed a navigation controller in a tab bar controller, see Adding a Navigation Controller to a Tab Bar Interface.

  6. Configure the tab bar item for each view controller.

    You can select the tab bar item from the tab bar controller edit surface or from the Interface Builder document window when it is in outline or browser mode. Using Interface Builder, you can specify the title, image, and badge of a tab bar item. Alternatively, you can set the tab bar item to one of the standard system tabs by assigning a value to the Identifier property in the Attributes inspector.

  7. In the applicationDidFinishLaunching: method of your application delegate, add the tab bar controller’s view to your main window.

    The tab bar controller does not install itself automatically in your application’s window. You must do this programmatically with code similar to the following:

    - (void)applicationDidFinishLaunching:(UIApplication *)application {
        [window addSubview:myTabBarController.view];
    }
  8. Save your nib file.

You should not need to configure the tab bar view that is added to your nib file with the tab bar controller. This bar does not contain any style options and all other options are managed for you by the tab bar controller object.

For additional information about using Interface Builder to configure nib files, see Interface Builder User Guide.

Creating a Tab Bar Interface Programmatically

If you prefer to create a tab bar controller programmatically, the most appropriate place to do so is in the applicationDidFinishLaunching: method of your application delegate. Because a tab bar controller usually provides the root view of your application’s window, you need to create it immediately after launch and before you show the window. The steps for creating a tab bar interface are as follows:

  1. Create a new UITabBarController object.

  2. Create a custom root view controller object for each tab.

  3. Add the root view controllers to an array and assign that array to your tab bar controller’s viewControllers property.

  4. Add the tab bar controller’s view to your application’s main window.

Listing 4-1 shows the basic code needed to create and install a tab bar controller interface in the main window of your application. This example creates only two tabs but you could create as many tabs as needed by creating more view controller objects and adding them to the controllers array. You would need to replace the custom view controller names MyViewController and MyOtherViewController with classes from your own application.

Listing 4-1  Creating a tab bar controller from scratch

- (void)applicationDidFinishLaunching:(UIApplication *)application {
   tabBarController = [[UITabBarController alloc] init];
 
   MyViewController* vc1 = [[MyViewController alloc] init];
   MyOtherViewController* vc2 = [[MyOtherViewController alloc] init];
 
   NSArray* controllers = [NSArray arrayWithObjects:vc1, vc2, nil];
   tabBarController.viewControllers = controllers;
 
   // Add the tab bar controller's current view as a subview of the window
   [window addSubview:tabBarController.view];
}

Creating a Tab Bar Item Programmatically

For each root view controller in your tab bar interface, you must provide a UITabBarItem object with the image and text to be displayed in the corresponding tab. You can associate tab bar items with your view controllers at any time before displaying your tab bar interface. You do this by assigning the tab bar item to the tabBarItem property of the corresponding view controller. The ideal time to create a tab bar item is during the initialization of the view controller itself but this is typically feasible only for custom view controllers. You could just as easily create and initialize the view controller object, create the tab bar item, and then make the association.

Listing 4-2 shows an example of how to create a tab bar item for a custom view controller. Because it is associated with a custom view controller, the view controller creates the tab bar item itself as part of its initialization process. In this example, the tab bar item includes both a custom image (which is stored in the application’s bundle) and a custom title string.

Listing 4-2  Creating the view controller’s tab bar item

- (id)init {
   if (self = [super initWithNibName:@"MyViewController" bundle:nil]) {
      self.title = @"My View Controller";
 
      UIImage* anImage = [UIImage imageNamed:@"MyViewControllerImage.png"];
      UITabBarItem* theItem = [[UITabBarItem alloc] initWithTitle:@"Home" image:anImage tag:0];
      self.tabBarItem = theItem;
      [theItem release];
   }
   return self;
}

If you are loading your tab bar interface from a nib file, you can also create your tab bar items using Interface Builder. For more information about including tab bar controllers (and tab bar items) in a nib file, see Creating a Tab Bar Interface Using a Nib File.

Managing Tabs at Runtime

After creating your tab bar interface, there are several ways to modify it and respond to changes in your application. You can add and remove tabs or use a delegate object to prevent the selection of tabs based on dynamic conditions. You can also add badges to individual tabs to call the user’s attention to that tab. The following sections show you how to take advantage of these features in your application.

Adding and Removing Tabs

If the number of tabs in your tab bar interface can change dynamically, you can make the appropriate changes at runtime as needed. You change the tabs at runtime in the same way you specify tabs at creation time, by assigning the appropriate set of view controllers to your tab bar controller. If you are adding or removing tabs in a way that might be seen by the user, you can animate the tab changes using the setViewControllers:animated: method.

Listing 4-3 shows a method that removes the currently selected tab in response to a tap in a specific button in the same tab. This method is implemented by the root view controller for the tab. You might use something similar in your own code if you want to remove a tab that is no longer needed. For example, you could use it to remove a tab containing some user-specific data that needs to be entered only once.

Listing 4-3  Removing the current tab

- (IBAction)processUserInformation:(id)sender
{
   // Call some app-specific method to validate the user data.
   // If the custom method returns YES, remove the tab.
   if ([self userDataIsValid])
   {
      NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.tabBarController.viewControllers];
      [newArray removeObject:self];
 
      [self.tabBarController setViewControllers:newArray animated:YES];
   }
}

Preventing the Selection of Tabs

If you need to prevent the user from selecting a tab, you can do so by providing a delegate object and implementing the tabBarController:shouldSelectViewController: method on that object. Preventing the selection of tabs should be done only on a temporary basis, such as when a tab does not have any content. For example, if your application requires the user to provide some specific information, such as a login name and password, you could disable all tabs except the one that prompts the user for the required information. Listing 4-4 shows an example of what such a method would look like. The hasValidLogin method is a custom method that you would implement to validate the provided information.

Listing 4-4  Preventing the selection of tabs

- (BOOL)tabBarController:(UITabBarController *)aTabBar
         shouldSelectViewController:(UIViewController *)viewController
{
   if (![self hasValidLogin] && (viewController != [aTabBar.viewControllers objectAtIndex:0]) )
   {
      // Disable all but the first tab.
      return NO;
   }
 
   return YES;
}

Monitoring User-Initiated Tab Changes

There are two types of user-initiated changes that can occur on a tab bar:

  • The user can select a tab.

  • The user can rearrange the tabs.

Both types of changes are reported to the tab bar controller’s delegate, which is an object that conforms to the UITabBarControllerDelegate protocol. You might provide a delegate to keep track of user changes and update your application’s state information accordingly. However, you should not use these notifications to perform work that would normally be handled by the view controllers being hidden and shown. For example, you would not use your tab bar controller delegate to change the appearance of the status bar to match the style of the currently selected view. Visual changes of that nature are best handled by your custom view controllers.

For more information about the methods of the UITabBarControllerDelegate protocol and how you use them, see UITabBarControllerDelegate Protocol Reference.

Preventing the Customization of Tabs

The More view controller provides built-in support for the user to modify the items displayed in the tab bar. For applications with lots of tabs, this support allows the user to pick and choose which screens are readily accessible and which require additional navigation to reach. The left side of Figure 4-6 shows the More selection screen displayed by the iPod application. When the user taps the Edit button in the upper-left corner of this screen, the More controller automatically displays the configuration screen shown on the right. From this screen, the user can replace the contents of the tab bar by dragging new items to it.

Figure 4-6  Configuring the tab bar of the iPod application

While it is a good idea to let the user rearrange tabs most of the time, there may be situations where you might not want the user to remove specific tabs from the tab bar or place specific tabs on the tab bar. In these situations, you can assign an array of view controller objects to the customizableViewControllers property. This array should contain the subset of view controllers that it is all right to rearrange. View controllers not in the array are not displayed on the configuration screen and cannot be removed from the tab bar if they are already there.

Changing a Tab’s Badge

The appearance of a tab in a tab bar interface normally does not change, except when it is selected. However, if you want to call attention to a specific tab, perhaps because there is new content on that tab for the user to look at, you can do so with a badge.

A badge is a small red marker displayed in the corner of the tab. Inside the badge is some custom text that you provide. Typically, badges contain numerical values reflecting the number of new items available on the tab, but you can also specify very short character strings too. Figure 4-7 shows badges for tabs in the Phone application.

Figure 4-7  Badges for tab bar items

To assign a badge to a tab, assign a non-nil value to the badgeValue property of the corresponding tab bar item. For example, a view controller that displays the number of new items in its badge might use code similar to the following to create the badge value. (Note that the numberOfNewItems property in the example is a fictitious property implemented by the view controller to track the number of new items. To implement the example in your own code, you would replace references to that property with an appropriate value from your own view controller.)

if (self.numberOfNewItems == 0)
   self.tabBarItem.badgeValue = nil;
else
   self.tabBarItem.badgeValue = [NSString stringWithFormat:@"%d", self.numberOfNewItems];

It is up to you to decide when to display badge values and to update the badge value at the appropriate times. However, if your view controller contains a property with such a value (such as the fictitious numberOfNewItems property in the preceding example), you can use KVO notifications to detect changes to the value and update the badge accordingly. For information about setting up and handling KVO notifications, see Key-Value Observing Programming Guide.

Tab Bar Controllers and View Rotation

Tab bar controllers support a portrait orientation by default and do not rotate to a landscape orientation unless all of the root view controllers support such an orientation. When a device orientation change occurs, the tab bar controller queries its array of view controllers. If any one of them does not support the orientation, the tab bar controller does not change its orientation.

Tab Bars and Full-Screen Layout

Tab bar controllers support full-screen layout differently from the way most other controllers support it. You can still set the wantsFullScreenLayout property of your custom view controller to YES if you want its view to underlap the status bar or a navigation bar (if present). However, setting this property to YES does not cause the view to underlap the tab bar view. The tab bar controller always resizes your view to prevent it from underlapping the tab bar.

For more information on full-screen layout for custom views, see Adopting a Full-Screen Layout for Custom Views.