Navigation Buttons in iPhone

The UINavigationController class allows an application to maintain a stack of UIViewControllers, each having their own view. Each UIViewController has a title property (an NSString) which represents the title to be displayed in the navigation bar of that view controller. But the navigation bar itself maintains an array of bar buttons that we can manipulate in any way we want. Let’s see how it works.

Start Xcode and choose “Create a new Xcode project.” Choose the Empty Application template, and click Next. Name the project “BarButtons” and select the options shown here:

Click Next, choose a location to save the project, and click Create.

When the project is done loading, select the AppDelegate.m file, then right click it and choose “New File” from the popup menu. (The reason we select the AppDelegate file first is that Xcode will add the new file immediately below whatever file we’ve selected in the navigator.)

We need a view controller for our application, so let’s add one now. In the template window, choose “Objective-C class” and click next. Name the class “View1Controller” and make sure that it is a subclass of UIViewController. Choose the options shown below, then click Next.

Save the new class in the default location by clicking Create. The Navigation panel should now look something like this:

Xcode offers many ways to build applications. Applications using UINavigationControllers can be easily built by using storyboards, but to get a good understanding of how navigation controllers work, we’ve elected to start with an empty template and not to use storyboards.

Perhaps the best way to think of a UINavigationController is as a stack of UIViewController objects. Each view controller has a view, and is a member of the navigation controller’s stack. At any given time, only the view belonging to the view controller on the top of the stack is visible.

Open up the AppDelegate.h file, and make the changes shown here:

#import <UIKit/UIKit.h>
#import "View1Controller.h"

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navController;
@property (strong, nonatomic) View1Controller *rootViewController;

@end

Note that we’ve added two properties, one for the UINavigationController (called navController) and a second for our View1Controller (calling it rootViewController). Importing View1Controller.h allows us to add this second property.

In the AppDelegate.m file, make these changes:

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize window = _window;
@synthesize navController = _navController;
@synthesize rootViewController = _rootViewController;

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
   
    // initialize the root view controller:
    self.rootViewController = [[View1Controller alloc] initWithNibName:nil bundle:nil];
    // set the title:
    self.rootViewController.title = @"Main View";
    // initialize the nav controller with the root vc as its root view controller:
    self.navController = [[UINavigationController alloc] initWithRootViewController:self.rootViewController];
    // make the nav controller a subview of the window:
   
    [self.window addSubview:self.navController.view];
   
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

As always, we first synthesize our properties. All other changes in this file will be made in the application: didFinishLaunchingWithOptions: method (leave the rest of the methods in place, we won’t alter those).

The first line in the method allocates memory for the main window, and initializes its area to the bounds of the main screen. Next, we set up our rootViewController object. Since this view controller was created with a nib in the main bundle, we initialize it with a nib name of nil (meaning “use the nib file it already has”) and a bundle of nil (meaning “use this application’s bundle”). We set the title property of this view controller to @”Main View” – the title will be displayed in the navigation bar when this view controller is displayed.

Next, we initialize the navigation controller, setting its rootViewController to the view controller we just instantiated. Every UINavigationController must have at least one view controller on its stack; the first view controller on the navigation controller’s stack is called its “Root View Controller.”

After setting up the navigation controller, we add its view as a subview of the main window. But what is the navigation controller’s view? It is the view belonging to its root view controller, which is the (currently empty) View1Controller.xib file.

Open the View1Controller.xib file, and change the background color of the view to some attractive color:

Since this view will be displayed with a title bar, we’re just going to leave it blank.

If we run the application now, this is what we should see:

The navigation controller gives us the title bar, and the view controller’s title is displayed inside it.

Now we’re going to add a bar button item to the navigation bar. Open up the View1Controller, and make the changes shown to the initWithNibName: bundle: method:

(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
       
        UIBarButtonItem *addButton = [[UIBarButtonItem alloc]           initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self                                        action:@selector(addTouched)];
        self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:addButton, nil];
 
    }
    return self;
}

We create a new instance of UIBarButtonItem called addButton by allocating, then initializing it with the UIBarButtonSystemItemAdd button. There are many different system buttons we can add; they are listed in the UIBarButtonItem documentation. The target for the button’s Touch Up Inside event is “self” (meaning this view controller), and the action is a selector called “addTouched” which we will define in a moment.

After creating the bar button item, we can display it by adding it to the navigationItem’s rightBarButtonItems array. (We could also add it to the leftBarButtonItems array, but since that array already contains the “back” button (if any), we should stick with the rightBarButtonItems array for new buttons.

Each view controller on the navigation controller’s stack maintains its own arrays of bar button items, by the way. When we push a new view controller on the stack, (as we’ll see in a moment), we’ll need to add bar button items to that view controller’s navigation item if we need them.

Add the addTouched method to the View1Controller.m file now:

(void)addTouched
{
    UIViewController *newController = [[UIViewController alloc] initWithNibName:nil bundle:nil];
    newController.title = @"New View";
    UIView *newView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    newView.backgroundColor = [UIColor yellowColor];
    newController.view = newView;
    [self.navigationController pushViewController:newController animated:YES];
    newView = nil;
    newController = nil;
}

Here, we get a new UIViewController instance called newController. Notice that we initialize this controller with a nib name of nil; this is because this particular controller is not associated with any nib file: we’re going to add a UIView object directly to the controller as its view.

We set the title of our controller to @”New View”, then we get a new UIView instance, the size of the entire screen. This view is then made the newController’s view. Finally, we push this view controller onto the navigation controller’s stack. The newController object now has a strong reference to the newView object, and the navigation controller has a strong reference to the newController object. We can now set both of these new objects to nil, because strong references are held elsewhere.

Run the app, and see what happens when we touch the add button in the nav bar:

When the “Main View” button is pushed in the New View controller’s title bar, the newController is popped off the stack; its strong reference is gone, so ARC cleans up the objects (both the controller and the view)!

Try adding bar buttons of different types to see what they look like. Also try different selector implementations. You can add several bar button items to the nav bar array. Remember though that on iPhone we have only a very limited space in which to work. On iPad, we have more space, but adding too many buttons to the nav bar itself can be confusing. Navigation controllers can also display a button bar at the bottom of their area as well, but that’s the subject of another blog…

Leave a Reply

Your email address will not be published. Required fields are marked *