iPhone Development 101

iPhone 101

Code Tips






Learn iPhone Programming:
Navigation Controllers

This tutorial was written for Xcode 3.2; it's outdated now. For a more up-to-date tutorial, check out Apple's App Development Tutorial to learn how to write iOS apps. There are also lots of other excellent books and online tutorials you can learn from.

If you'd like to find out when the updated version of this class is ready, follow me on twitter:

A Navigation Controller is a special kind of view controller that manages a stack of view controllers and their corresponding views. It's an ideal way to display heirarchical data.

The Navigation Controller is always initialized with a root view controller; this will be the starting view at the bottom of the stack. As the user presses buttons in that view, you can then push a new view controller onto the stack to show a new view. When the user is done with the new view and presses a button to go back, you then pop that controller off the stack to return to the root view.

A Navigation Controller can be used with or without a Navigation Bar, which appears at the top of the screen:

When you push a new view onto the stack, the Navigation Bar will automatically show a "back" button on the left side of the bar that will take the user back to the previous view.

Let's rework the flower project to use a Navigation Controller. You can either edit the existing Second project, or make a copy of it and rename it. I've chosen to rename it to NavTestApp; click here to see how to rename a project in Xcode.

Declaring a Navigation Controller

The Navigation controller will be the foundation of your view heirarchy in this app, so it gets created first. Declare a navigation controller instance variable in your app delegate's header file (NavTestAppDelegate.h), and create a @property for it:

@interface NavTestAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
    UINavigationController *navController;

@property (nonatomic, retain) UINavigationController *navController;
@property (nonatomic, retain) IBOutlet UIWindow *window;


Complete the setup of the navController property by @synthesizing it in the delegate implementation file (NavTestAppDelegate.m):

@synthesize window, navController;

and releasing it in the dealloc method:

- (void)dealloc {
    [window release];
    [navController release];
    [super dealloc];

Creating the Navigation Controller Instance

If you look at the UINavigationController documentation, you'll see that it has an initWithRootViewController method. You're going to use that to create your nav controller instance, and use the MyViewController instance as the root view controller:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    [self.window makeKeyAndVisible];
    // create the MyView controller instance:
    MyViewController *controller = [[MyViewController alloc] initWithNibName:@"MyView" bundle:nil];

    // set the title that appears in the navigation bar:
    [controller.navigationItem setTitle:@"Main View"];
    // create the Navigation Controller instance:
    UINavigationController *newnav = [[UINavigationController alloc] initWithRootViewController:controller];
    // set the navController property:
    [self setNavController:newnav];
    // release both controllers:
    [newnav release];
    [controller release];

    // add the Navigation Controller's view to the window:
    [window addSubview:[navController view]];

    return YES;

Pushing Views Onto The Stack

To add a new view controller to the stack (and make its view the top view), use the pushViewController method. Edit MyViewController.m and modify the showImage method:

- (IBAction)showImage:(id)sender {
    FlowerViewController *controller = [[FlowerViewController alloc] initWithNibName:@"FlowerView" bundle:[NSBundle mainBundle]];
    [controller.navigationItem setTitle:@"Second View"];
    [self.navigationController pushViewController:controller animated:YES];
    [controller release];

You can also remove the flowerImageView instance variable and property from MyViewController. Since the navigation controller retains all of the controller instances pushed onto the stack, you don't need to retain them separately.

Popping Views Off Of The Stack

If you're using a navigation bar, you'll automatically get a back button that allows the user to navigate back to the previous view. However if you're not using a nav bar, or if you want to allow another button to close the view, simply call the popViewController method. Edit FlowerViewController.m and change the closeButton method:

-(IBAction)closeButton:(id)sender {
    [self.navigationController popViewControllerAnimated:YES];

Hiding the Navigation Bar

Build and run the NavTest project. You should be able to click the Show Image button and see the flower image view appear. However one problem you'll notice is that the navigation bar has pushed the views down so they don't fit within the iPhone's window. The "Show Image" button is halfway cut off at the bottom of the screen:

There are two options here: either you can hide the navigation bar, or you can go into Interface Builder and resize your views so they fit within the dimensions of the window. (See the various sizes of iPhone UI elements.) The height of a navigation bar is 44 points, so your views need to be 416 points high.

To hide the navigation bar, call the setNavigationBarHidden:animated: method when creating the nav controller instance:

    // create the Navigation Controller:
    UINavigationController *newnav = [[UINavigationController alloc] initWithRootViewController:controller];
    [newnav setNavigationBarHidden:YES animated:NO];

If you hide the navigation bar, you'll have to be sure to include Back or Close buttons in each view to allow the user to navigate back through the stack.

Additional References