Navigation Controllers

You use navigation controllers to manage the presentation of hierarchical data in your application. A navigation controller manages a self-contained view hierarchy (known as a navigation interface) whose contents are composed partly of views managed directly by the navigation controller and partly of views managed by custom view controllers you provide. Each custom view controller manages a distinct view hierarchy but a navigation controller coordinates the navigation between different view hierarchies.

Although a navigation interface consists mostly of your custom content, there are still places where your code must interact directly with the navigation controller object. In addition to telling the navigation controller when to display a new view, you are responsible for configuring the navigation bar—the view at the top of the screen that provides context about the user’s place in the navigation hierarchy. You can also provide items for a toolbar that is managed by the navigation controller.

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

Anatomy of a Navigation Interface

Although its primary job is to manage the presentation of your custom view controllers, a navigation controller is also responsible for presenting some custom views of its own. Specifically, it presents a navigation bar, which contains a back button along with some buttons you can customize. In iOS 3.0 and later, a navigation controller can also present a navigation toolbar view and populate it with custom buttons; display of the toolbar is optional though.

Figure 3-1 shows the key views of a navigation interface. The navigation view in this figure is the view stored in the navigation controller’s view property. This is the view that you get and embed in a window or present using another view controller. All of the other views in the interface are part of what is essentially an opaque view hierarchy managed by the navigation controller.

Figure 3-1  The views of a navigation interface

Although the navigation bar and toolbar are customizable views, you must never modify the views in the navigation hierarchy directly. The only way to customize these views is through specific interfaces of the UINavigationController and UIViewController classes. For information on how to customize the contents of the navigation bar, see Customizing the Navigation Bar Appearance. For information about how to display and configure custom toolbar items in a navigation interface, see Displaying a Navigation Toolbar.

The Objects of a Navigation Interface

A navigation controller uses several objects to implement the navigation interface. You are responsible for providing some of these objects but the rest are created by the navigation controller itself. Specifically, you are responsible for providing the view controllers with the custom content you want to present. If you want to respond to notifications from the navigation controller, you can also provide a delegate object. However, the navigation controller creates the additional views (such as the navigation bar and toolbar) that are used for the navigation interface and is responsible for managing those views. Figure 3-2 shows the relationship between the navigation controller and these key objects.

Figure 3-2  Objects managed by the navigation controller

Except for modifying some aspects of their appearance, you should not modify the navigation bar or toolbar objects associated with a navigation controller. The navigation controller alone is responsible for configuring and displaying them. In addition, a navigation controller object automatically assigns itself as the delegate of its UINavigationBar object and prevents other objects from changing that relationship.

Among the objects it is all right for you to modify are the delegate and the other view controllers on the navigation stack. The navigation stack is a last-in, first-out collection of custom view controller objects that is managed by the navigation controller. The first item added to the stack becomes the root view controller and can never be removed. Additional items can be added to the stack using the methods of the UINavigationController class.

Figure 3-3 shows the relevant relationships between the navigation controller and the objects on the navigation stack. It is important to note that the objects in the topViewController and visibleViewController properties need not be the same. If you present a view controller modally from the topmost object in the stack, the topViewController property does not change but the object in the visibleViewController property does. Specifically, the value in the visibleViewController property changes to reflect the modal view controller that was presented.

Figure 3-3  The navigation stack

Your main responsibility is to push new view controllers onto the stack in response to user actions. Each view controller you push on the navigation stack is responsible for presenting some portion of your application’s data. Typically, when the user selects an item in the currently visible view, you create a new view controller object, assign the data for the selected item to it, and push the new view controller onto the stack. Doing so is how you present the selected data to the user. For example, when the user selects a photo album, the Photos application pushes a view controller that displays the photos in that album. In most cases, you do not have to pop view controllers off the stack programmatically. Instead, the navigation controller provides a back button on the navigation bar, that when tapped, pops the topmost view controller automatically.

For more information about how to customize the navigation bar, see Customizing the Navigation Bar Appearance. For information about pushing view controllers onto the navigation stack (and removing them later), see Modifying the Navigation Stack. For information on how to customize the contents of the toolbar, see Displaying a Navigation Toolbar.

Creating a Navigation Interface

You use a navigation interface in situations where the information you want to present is organized hierarchically. Typically, you use a navigation controller to manage the presentation of hierarchical data but you could also use one to manage multilevel editing or some other interface that required multiple successive screens.

Before creating a navigation interface, you need to decide how you intend to use it. There are a handful of places where you might install a navigation interface in your application:

In the first three scenarios, the navigation controller provides a crucial part of your basic interface and stays in place until the application exits. However, the last two scenarios reflect a more temporary use for navigation controllers, in which case the process for using the navigation controller is identical to the process for other modal and custom view controllers. The only difference is that the navigation controller continues to provide additional navigation features not available with a single custom view controller. Although the following sections focus on how you create the more permanent types of navigation interface, most of the customization steps and general information apply to all navigation controllers, regardless of how you intend to use them.

Defining the Custom View Controllers for a Navigation Interface

One of the key things you must do to implement a navigation interface is decide what data you plan to present at each stage. Every navigation interface has at least one level of data that represents the root level of data. This is the starting point of your interface. For example, the Photos application displays the list of available photo albums at the root level of its data hierarchy. Selecting a photo album then displays the photos in that album and selecting a photo shows a larger version of the photo.

For each level of your data hierarchy, you must provide a custom view controller object to manage and present the data at that level. If the presentation at multiple levels is essentially the same, you can reuse the same view controller class if you want. However, at runtime, you must still create separate instances of that class and configure each one to manage its own set of data. For example, the Photos application has three distinct presentation types, so it would need to be three separate view controller objects, such as the ones shown in Figure 3-4.

Figure 3-4  Defining view controllers for each level of data

With the exception of view controllers managing leaf data, each custom view controller must provide a way for the user to navigate to the next level of the data hierarchy. A view controller that displays a list of items can use taps in a given table cell to display the next level of data. For example, when a user selects a photo album from the top-level list, the Photos application creates a new photo album view controller. The new view controller is initialized with enough information about the album for it to present the relevant photos.

If your application has highly structured data, with a known progression from one level to the next, defining the view controllers at each level (and the relationships between them) should be relatively straightforward. (The Photos application always displays albums, followed by the contents of an album, followed by a single photo.) But if your application presents data differently depending on which item is selected at the current level, you might want to use additional information in your data model to determine how to present that data. You could use that information to determine whether to use different view controller classes or to customize the presentation style of a single view controller class. For example, viewing songs by Composer in the iPod application displays a list of albums or a list of songs depending on whether the songs for that composer come from the same album or from several different albums. In both cases though, the data is presented in list form using a table; therefore, you could use a single view controller but simply provide different table cells for each type of data.

For general information and guidance on defining custom view controllers, see Custom View Controllers.

Loading Your Navigation Interface from a Nib File

Although Interface Builder supports the inclusion of navigation controller objects in nib files, the semantics are slightly different than they are 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, with a navigation controller, the view is always created programmatically so there is no view to put in a separate nib file. One consequence of this behavior is that a navigation controller never manages a nib file—in other words, you never assign a navigation controller to the File’s Owner placeholder. Instead, the only time you mix navigation controllers and nib files is when the navigation controller itself is stored in the nib file. For this reason, a navigation controller is almost always a passenger in a nib file managed by some other object.

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

Figure 3-5 shows the configuration of the main nib file for an application that displays a navigation interface in its main window. The nib file includes the window object, the navigation controller, and the root view controller for the navigation interface, which is embedded inside the navigation controller itself. (The view managed by the root view controller typically would not need to be in the same nib file, and in this example is shown in a separate nib file.)

Figure 3-5  Nib file containing a navigation interface

Because the navigation controller creates its view programmatically, you cannot install that view in a window using Interface Builder. Instead, you must also add the view to a window programmatically. If the window and navigation controller themselves are in a nib file, then you must remember to store references to those objects so that you can access them later.

To configure a navigation controller in a nib file, you do the following:

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

    When you add a navigation controller, Interface Builder also adds a navigation bar, an embedded root view controller object, and a navigation item for your root view controller. You can access some of these objects by selecting them in the navigation controller edit surface. You can also select any of the objects from the document window when in outline and browser modes.

  2. Save a reference to the navigation controller using an outlet.

    In order to access the navigation 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 UINavigationController*  myNavigationController;
    }
    @end

    After adding the outlet definition, create a connection from that outlet to the navigation controller object in Interface Builder.

  3. Set the class of the root view controller to a custom class from your Xcode project.

    The class you choose should be the one responsible for displaying the highest-level data in your navigation hierarchy. If there are multiple levels of data, the view presented by your view controller class should include controls for navigating to the next level of data. For information about how to design your view controllers, see Defining the Custom View Controllers for a Navigation Interface.

  4. Configure the view for your root view controller.

    You can include the view in the same nib file or store it in a different nib file. Using a different nib file is recommended because it gives the system the option of removing the view from memory during low-memory conditions.

    To specify a separate nib file, set the NIB Name attribute of the root view controller to the name of your nib file. To include the views in the same nib file as the navigation controller, drag your view objects to the navigation controller edit surface.

  5. In the applicationDidFinishLaunching: method of your application delegate, add the navigation controller’s view to your main window.

    The navigation 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:myNavigationController.view];
    }
  6. Save your nib file.

In addition to configuring the root view controller object, you can also use the inspector to change the attributes of the navigation item and navigation bar that Interface Builder creates. For the navigation bar, you can change some basic stylistic attributes. For the navigation item, you can specify a custom title and prompt text. If you do not specify a custom string for the navigation item, Interface Builder uses the string in the custom view controller’s title property.

The navigation controller’s edit surface provides a convenient way to edit the configuration of the navigation bar. To add additional buttons or views to the navigation bar, drag the appropriate objects from the library to the navigation bar in the edit surface. Doing so adds the corresponding objects to the root view controller’s navigation item. You can then configure the items you dragged as you would other Interface Builder objects. For example, you could connect buttons in the navigation item to action methods of your view controller in order to facilitate the handling of user taps at runtime.

For information about using Interface Builder to configure the contents of your nib files, including how to connect views and other controls to action methods, see Interface Builder User Guide.

Creating a Navigation Interface Programmatically

If you prefer to create a navigation controller programmatically, you may do so from any appropriate point in your code. For example, if the navigation controller provides the root view for your application window, you could create the navigation controller in the applicationDidFinishLaunching: method of your application delegate.

There are other situations where it might make more sense to create a view controller programmatically. For example, if you plan to present a navigation interface modally, it is usually simpler to create the navigation controller object at the point of use, present it, and then release it when it is no longer needed. Loading such a view controller from a nib file would require extra overhead for loading the nib file and might require you to store a pointer to the navigation controller object for longer than necessary.

When creating a navigation controller, you must do the following:

  1. Create the root view controller for the navigation interface.

    This object is the top-level view controller in the navigation stack. The navigation bar displays no back button when its view is displayed and the view controller cannot be popped from the navigation stack.

  2. Create the navigation controller, initializing it using the initWithRootViewController: method.

  3. Add the navigation controller’s view to your window (or otherwise present it in your interface).

After you create the navigation controller, you can use it as appropriate. For example, you could add its view to your window, use it to initialize another view controller, or present it modally.

Listing 3-1 shows a simple implementation of the applicationDidFinishLaunching: method that creates a navigation controller and adds it to the application’s main window. The navigationController and window variables are member variables of the application delegate and the MyRootViewController class is a custom view controller class. When the window for this example is displayed, the navigation interface presents the view for the root view controller in the navigation interface.

Listing 3-1  Creating a navigation controller programmatically

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    UIViewController *rootController = [[MyRootViewController alloc] init];
    navigationController = [[UINavigationController alloc]
                                initWithRootViewController:rootController];
    [rootController release];
 
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    [window addSubview:navigationController.view];
    [window makeKeyAndVisible];
}

Adopting a Full-Screen Layout for Navigation Views

Typically, a navigation interface displays your custom content in the gap between the bottom of the navigation bar and the top of the toolbar or tab bar (see Figure 3-1). However, a view controller can ask that its view be displayed with a full-screen layout instead. In a full-screen layout, the content view is configured to underlap the navigation bar, status bar, and toolbar as appropriate. This lets you maximize the visible amount of content for the user and is useful for photo displays or other places where you might want more space.

When determining whether a view should be sized to fill all or most of the screen, a navigation controller considers several factors, including the following:

Each of these factors is used to determine the final size of the custom view. The order of the items in the preceding list also reflects the precedence by which each factor is considered. The window size is the first limiting factor; if your application’s main window (or the containing parent view in the case of a modally presented view controller) does not span the screen, the views it contains cannot hope to do so either. Similarly, if the navigation bar or toolbar are visible but not translucent, it does not matter if the view controller wants its view to be displayed using a full-screen layout. The navigation controller never displays content under an opaque navigation bar.

If you are creating a navigation interface and want your custom content to span most or all of the screen, here are the steps you should take:

  1. Configure the frame of your custom view to fill the screen bounds.

    Be sure to configure the autoresizing attributes of your view as well. The autoresizing attributes ensure that if your view needs to be resized, it adjusts its content accordingly. Alternatively, when your view is resized, you can call the setNeedsLayout method of your view to indicate that the position of its subviews should be adjusted.

  2. Set the translucent property of your navigation controller to YES. This allows your content to underlap the navigation bar.

  3. To underlap the status bar, set the wantsFullScreenLayout property of your view controller to YES. (The navigation bar must be translucent in order for this attribute to be recognized.)

  4. To underlap an optional toolbar, set the translucent property of the toolbar to YES.

When presenting your navigation interface, the window or view to which you add your navigation view must also be sized appropriately. If your application uses a navigation controller as its primary interface, then your main window should be sized to match the screen dimensions. In other words, you should set its size to match the bounds property of the UIScreen class (instead of the applicationFrame property). In fact, for a navigation interface, it is usually better to create your window with the full-screen bounds in all situations because the navigation controller adjusts the size of its views to accommodate the status bar automatically anyway.

If you are presenting a navigation controller modally, the content presented by that navigation controller is limited by the view controller doing the presenting. If that view controller does not want to underlap the status bar, then the modally presented navigation controller is not going to be allowed to underlap the status bar either. In other words, the parent view always has some influence over how its modally presented views are displayed.

For additional information on how to configure the interface to support full-screen layout, see Adopting a Full-Screen Layout for Custom Views.

Modifying the Navigation Stack

Although the navigation controller manages the navigation stack, you are responsible for creating the objects that reside on that stack. When initializing a navigation controller object, you must provide a custom view controller to display the root content of your data hierarchy. This root view controller always sits at the bottom of the navigation stack and can never be removed. After that, you can add or remove other view controllers either programmatically or in response to user interactions. To add and remove these view controllers, you use the methods of the UINavigationController class.

The navigation controller provides several options for managing the contents of the navigation stack. These options cover the various scenarios you are likely to encounter in your application. Table 3-1 lists these scenarios and how you respond to them.

Table 3-1  Options for managing the navigation stack

Scenario

Description

Display the next level of hierarchical data.

When the user selects an item displayed by the topmost view controller, you can use the pushViewController:animated: method to push a new view controller onto the navigation stack. The new view controller is responsible for presenting the contents of the selected item.

Back up one level in the hierarchy.

The navigation controller usually provides a back button to remove the topmost view controller from the stack and return to the previous screen. You can also remove the topmost view controller programmatically using the popViewControllerAnimated: method.

Restore the navigation stack to a previous state.

When your application launches, you can use the setViewControllers:animated: method to restore the navigation controller to a previous state. For example, you would use this method to return the user to the same screen they were viewing when they last quit the application.

In order to restore your application to a previous state, you must first save enough state information to recreate the needed view controllers. When the user quits your application, you would need to save some markers or other information indicating the user’s position in your data hierarchy. At the next launch time, you would then read this state information and use it to recreate the needed view controllers before calling the setViewControllers:animated: method.

Return the user to the root view controller.

To return to the top of your navigation interface, use the popToRootViewControllerAnimated: method. This method removes all but the root view controller from the navigation stack.

Back up an arbitrary number of levels in the hierarchy.

To back up more than one level at a time, use the popToViewController:animated: method. You might use this method in situations where you use a navigation controller to manage the editing of custom content (rather than presenting content modally). If the user decides to cancel the operation after you have pushed multiple edit screens, you can use this method to remove all of the editing screens at once, rather than one at a time.

Jump to an arbitrary location in your data hierarchy.

You can use the setViewControllers:animated: to jump seamlessly to anywhere in your data hierarchy. Although jumping around your data hierarchy is not a great idea (because of the potential confusion it might cause), using this method to provide a trail back to the root view controller is the best way to do it.

When you animate the pushing or popping of view controllers, the navigation controller automatically creates animations that make the most sense. For example, if you pop multiple view controllers off the stack using the popToViewController:animated: method, the navigation controller uses an animation only for the topmost view controller. All other intermediate view controllers are dismissed without an animation. If you push or pop an item using an animation, you must wait until the animation is complete before you attempt to push or pop another view controller.

Monitoring Changes to the Navigation Stack

As changes occur to the navigation stack, the navigation controller sends appropriate messages to its delegate. Specifically, whenever you push or pop a view controller, the navigation controller sends messages to the affected view controllers. Figure 3-6 shows the sequence of events that occurs during a push or pop operation and the corresponding messages that are sent to your custom objects at each stage. The new view controller reflects the view controller that is about to become the topmost view controller on the stack.

Figure 3-6  Messages sent during stack changes

You can use the methods of the navigation controller’s delegate to update the state of your navigation interface and modify your application’s data model. For example, if you are using a navigation interface to facilitate the editing of content, you might use the navigationController:willShowViewController:animated: method to save any changes before the corresponding view controller is dismissed. However, you would not use these methods to make changes to your views or view hierarchy. Those types of changes should be handled by your custom view controllers.

Customizing the Navigation Bar Appearance

A navigation bar is a container view that manages the controls commonly found in a navigation interface. Although it is essentially just a view object, navigation bars take on a special role when managed by a corresponding navigation controller object. To ensure consistency, and to reduce the amount of work needed to build navigation interfaces, each navigation controller object creates its own navigation bar and takes on most of the responsibility for managing that bar’s content. As needed, the navigation controller interacts with other objects (like your custom view controllers) to help in this process.

Because managing the navigation bar is the responsibility of the navigation controller, direct modification of the navigation bar itself is considered off limits for the most part. Be that as it may, there are still many ways in which you can customize the navigation bar to suit your needs. The following sections explain the structure of navigation bars and how you customize them for your application.

Configuring the Navigation Item Object

The structure of a navigation bar is similar to the structure of a navigation controller in many ways. Like a navigation controller, a navigation bar is a container for content that is provided by other objects. In the case of a navigation bar, the content is provided by one or more UINavigationItem objects, which are stored using a stack data structure known as the navigation item stack. Each navigation item provides a complete set of views and content to be displayed in the navigation bar. Unlike a navigation controller, a navigation bar is an actual view object that can be embedded inside other views.

Figure 3-7 shows some of the key objects related to a navigation bar at runtime. The owner of the navigation bar (whether it is a navigation controller or your custom code) is responsible for pushing items onto the stack and popping them off as needed. In order to provide proper navigation, the navigation bar maintains pointers to select objects in the stack. Although most of the navigation bar’s content is obtained from the topmost navigation item, a pointer to the back item is maintained so that a back button (with the title of the preceding item) can be created.

Figure 3-7  The objects associated with a navigation bar

When used in a navigation interface, the contents of the navigation bar’s stack always parallel the contents of the parent navigation controller’s stack. In other words, for each view controller in the navigation stack, there is a corresponding navigation item in the same position on the navigation item stack of the navigation bar. The reason for this one-to-one correspondence is that each view controller actually provides its own navigation item.

A navigation bar has three primary positions for placing items: left, right, and center. Table 3-2 lists the properties of the UINavigationItem class that are used to configure each of these positions. When configuring a navigation item for use with a navigation controller, be aware that custom controls in some positions may be ignored in favor of the expected controls. The description of each position includes information about how your custom objects are used.

Table 3-2  Item positions on a navigation bar

Position

Property

Description

Left

backBarButtonItem

leftBarButtonItem

In a navigation interface, the navigation controller assigns a Back button to the left position by default. To get the default Back button provided by the navigation controller, get the value of the backBarButtonItem property.

To assign a custom button or view to the left position, and thereby replace the default Back button, assign a UIBarButtonItem object to the leftBarButtonItem property.

Center

titleView

In a navigation interface, the navigation controller displays a custom view with the title of your view controller by default. You can replace this view as desired with a custom view of your choosing.

If you do not provide a custom title view, the navigation bar displays a custom view with an appropriate title string. The title string is obtained from the navigation item by default, or from the view controller if the navigation item does not provide an appropriate title.

Right

rightBarButtonItem

This position is open by default. It is typically used to place buttons for editing or modifying the current screen. You can also place custom views here as well by wrapping the view in a UIBarButtonItem object.

Figure 3-8 shows how the contents of the navigation bar are assembled for a navigation interface. The navigation item associated with the current view controller provides the content for the center and right positions of the navigation bar. The navigation item for the previous view controller provides the content for the left position. Although the left and right items require you to specify a UIBarButtonItem object, you can wrap a view in a bar button item as shown in the figure. If you do not provide a custom title view, the navigation item creates one for you using the title of the current view controller.

Figure 3-8  Navigation bar structure

Showing and Hiding the Navigation Bar

When used in conjunction with a navigation controller, you always use the setNavigationBarHidden:animated: method of UINavigationController to show and hide the navigation bar. You must never hide the navigation bar by modifying the UINavigationBar object’s hidden property directly. In addition to showing or hiding the bar, using the navigation controller method gives you more sophisticated behaviors for free. Specifically, if a view controller shows or hides the navigation bar in its viewWillAppear: method, the navigation controller animates the appearance or disappearance of the bar to coincide with the appearance of the new view controller.

Because the user needs the back button on the navigation bar to navigate back to the previous screen, you should never hide the navigation bar without giving the user some way to get back to the previous screen. The most common way to provide navigation support is to intercept touch events and use them to toggle the visibility of the navigation bar. For example, the Photos application does this when a single image is displayed full screen. You could also detect swipe gestures and use them to pop the current view controller off the stack, but such a gesture is less discoverable than simply toggling the navigation bar’s visibility.

Modifying the Navigation Bar Object Directly

In a navigation interface, a navigation controller owns its UINavigationBar object and is responsible for managing it. It is not permissible to change the navigation bar object or modify its bounds, frame, or alpha values directly. However, there are a few properties that it is permissible to modify, including the following:

Figure 3-9 shows how the barStyle and translucent properties affect the appearance of the navigation bar. For translucent styles, it is worth noting that if the main view of the underlying view controller is a scroll view, the navigation bar automatically adjusts the content inset value to allow content to scroll out from under the navigation bar. It does not make this adjustment for other types of views.

Figure 3-9  Navigation bar styles

If you want to show or hide the entire navigation bar, you should similarly use the setNavigationBarHidden:animated: method of the navigation controller rather than modify the navigation bar directly. For more information about showing and hiding the navigation bar, see Showing and Hiding the Navigation Bar.

Using Custom Buttons and Views as Navigation Items

To customize the appearance of the navigation bar for a specific view controller, modify the attributes of its associated UINavigationItem object. You can get the navigation item for a view controller from its navigationItem property. The view controller does not create its navigation item until you request it, so you should ask for this object only if you plan to install the view controller in a navigation interface.

If you choose not to modify the navigation item for your view controller, the navigation item provides a set of default objects that should suffice in many situations. Of course, any customizations you make take precedence over the default objects.

For the topmost view controller, the item that is displayed on the left side of the navigation bar is determined using the following rules:

  • If you assign a custom bar button item to the leftBarButtonItem property of the topmost view controller’s navigation item, that item is given the highest preference.

  • If you do not provide a custom bar button item and the navigation item of the view controller one level down on the navigation stack has a valid item in its backBarButtonItem property, the navigation bar displays that item.

  • If a bar button item is not specified by either of the view controllers, a default back button is used and its title is set to the value of the title property of the previous view controller—that is, the view controller one level down on the navigation stack. (If the topmost view controller is the root view controller, no default back button is displayed.)

For the topmost view controller, the item that is displayed in the center of the navigation bar is determined using the following rules:

  • If you assign a custom view to the titleView property of the topmost view controller’s navigation item, the navigation bar displays that view.

  • If no custom title view is set, the navigation bar displays a custom view containing the view controller’s title. The string for this view is obtained from the title property of the view controller’s navigation item. If the value of that property is nil, the string from the title property of the view controller itself is used.

For the topmost view controller, the item that is displayed on the right side of the navigation bar is determined using the following rules:

  • If the new top-level view controller has a custom right bar button item, that item is displayed. To specify a custom right bar button item, set the rightBarButtonItem property of the navigation item.

  • If no custom right bar button item is specified, the navigation bar displays nothing on the right side of the bar.

To add some custom prompt text above the navigation bar controls, assign a value to the prompt property of the navigation item.

Figure 3-10 shows some different navigation bar configurations, including several that use custom views and prompts. The navigation bars in this figure are from the sample project NavBar: Customizing UINavigationBar's appearance.

Figure 3-10  Custom buttons in the navigation bar

Listing 3-2 shows the code from the NavBar application that would be required to create the third navigation bar in Figure 3-10, which is the navigation bar containing the right bar button item with a custom view. Because it is in the right position on the navigation bar, you must wrap the custom view with a UIBarButtonItem object before assigning it to the rightBarButtonItem property.

Listing 3-2  Creating custom bar button items

// View 3 - Custom right bar button with a view
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:
                                      [NSArray arrayWithObjects:
                                          [UIImage imageNamed:@"up.png"],
                                          [UIImage imageNamed:@"down.png"],
                                          nil]];
 
[segmentedControl addTarget:self action:@selector(segmentAction:) forControlEvents:UIControlEventValueChanged];
segmentedControl.frame = CGRectMake(0, 0, 90, kCustomButtonHeight);
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.momentary = YES;
 
defaultTintColor = [segmentedControl.tintColor retain];    // keep track of this for later
 
UIBarButtonItem *segmentBarItem = [[UIBarButtonItem alloc] initWithCustomView:segmentedControl];
[segmentedControl release];
 
self.navigationItem.rightBarButtonItem = segmentBarItem;
[segmentBarItem release];

Configuring your view controller’s navigation item programmatically is the most common approach for most applications. Although you could create bar button items using Interface Builder, it is often much simpler to create them programmatically. You should create the items in the viewDidLoad method of your view controller.

Using Edit and Done Buttons

Views that support in-place editing can include a special type of button in their navigation bar that allows the user to toggle back and forth between display and edit modes. The editButtonItem method of UIViewController returns a preconfigured button that when pressed toggles between an Edit and Done button and calls the view controller’s setEditing:animated: method with appropriate values. To add this button to your view controller’s navigation bar, you would use code similar to the following:

myViewController.navigationItem.rightBarButtonItem = [myViewController editButtonItem];

If you include this button in your navigation bar, you must also override your view controller’s setEditing:animated: method and use it to adjust your view hierarchy. For more information on implementing this method, see Enabling Edit Mode for a View.

Displaying a Navigation Toolbar

In iOS 3.0 and later, a navigation interface can display a toolbar and populate it with items provided by the currently visible view controller. The toolbar itself is managed by the navigation controller object. Supporting a toolbar at this level is necessary in order to create smooth transitions between screens. When the topmost view controller on the navigation stack changes, the navigation controller animates changes between different sets of toolbar items. It also creates smooth animations in cases where you want to toggle the visibility of the toolbar for a specific view controller.

To configure a toolbar for your navigation interface, you must do the following:

Figure 3-11 shows an example of how the objects you associate with your custom view controller are reflected in the toolbar. Items are displayed in the toolbar in the same order they are provided in the array. The array can include all types of bar button items, including fixed and flexible space items, system button items, or any custom button items you provide. In this example, the five items are all button items from the Mail application.

Figure 3-11  Toolbar items in a navigation interface

Specifying the Toolbar Items

When configuring bar button items, always remember to associate an appropriate target and action with the button. The target and action information is what you use to respond to taps in the toolbar. In most cases, the target should be the view controller itself, since it is responsible for providing the toolbar items.

Figure 3-12 shows a sample toolbar that places a segmented control in the center of the toolbar and Listing 3-3 shows the code needed to configure such a toolbar. You would implement the method in your view controller and call it at initialization time.

Figure 3-12  A segmented control centered in a toolbar

Listing 3-3  Configuring a toolbar with a centered segmented control

- (void)configureToolbarItems
{
   UIBarButtonItem *flexibleSpaceButtonItem = [[UIBarButtonItem alloc]
                        initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                        target:nil action:nil];
 
   // Create and configure the segmented control
   UISegmentedControl *sortToggle = [[UISegmentedControl alloc]
                        initWithItems:[NSArray arrayWithObjects:@"Ascending",
                                        @"Descending", nil]];
   sortToggle.segmentedControlStyle = UISegmentedControlStyleBar;
   sortToggle.selectedSegmentIndex = 0;
   [sortToggle addTarget:self action:@selector(toggleSorting:)
               forControlEvents:UIControlEventValueChanged];
 
   // Create the bar button item for the segmented control
   UIBarButtonItem *sortToggleButtonItem = [[UIBarButtonItem alloc]
                                    initWithCustomView:sortToggle];
   [sortToggle release];
 
   // Set our toolbar items
   self.toolbarItems = [NSArray arrayWithObjects:
                         flexibleSpaceButtonItem,
                         sortToggleButtonItem,
                         flexibleSpaceButtonItem,
                         nil];
 
   [sortToggleButtonItem release];
   [flexibleSpaceButtonItem release];
}

In addition to setting toolbar items during initialization, a view controller can also change its existing set of toolbar items dynamically using the setToolbarItems:animated: method. This method is useful for situations where you want to update the toolbar commands to reflect some other user action. For example, you could use it to implement a set of hierarchical toolbar items, whereby tapping a button on the toolbar displays a set of related child buttons.

Showing and Hiding the Toolbar

To hide the toolbar for a specific view controller, set the hidesBottomBarWhenPushed property of that view controller to YES. When the navigation controller encounters a view controller with this property set to YES, it generates an appropriate transition animation whenever the view controller is pushed onto (or removed from) the navigation stack.

If you want to hide the toolbar sometimes (but not always), you can call the setToolbarHidden:animated: method of the navigation controller at any time. A common way to use this method is to combine it with a call to the setNavigationBarHidden:animated: method to create a temporary full-screen view. For example, the Photos application toggles the visibility of both bars when it is displaying a single photo and the user taps the screen.