How to embed a navigation controller inside a tab bar controller

It’s a commonly used design pattern in the iPhone world to combine tab bar and navigation controllers. In the official Apple developer docs you can find some clues how to solve this in a programmatically way, but you can only rarely find a good tutorial for doing this in Interface Builder (IB). So I’ll try to walk you trough all the steps for building a good application framework with one tab bar and one navigation controller.

If you like this tutorial, show some love and flattr it :)

Okay, let’s do this

First of all, launch your Xcode IDE, start a new iPhone window based project (we could use the tab bar based template but we want to create it from scratch) and call it “Tabs”. In “Groups & Files” expand the Resources folder and double-click MainWindow.xib to launch IB. Open the Library and drag a “Tab Bar Controller” onto the little Window named MainWindow.xib. The controller will appear on the workspace and a window with a tab bar layout will pop up.

Screen01

By the way: You can close the blank window layout, we won’t need it here ;) So far so good. We will now fill the two tabs that were created for us. But before that we need to create an outlet for the controller in the Tabs App Delegate object (that orange cube in the left window). We will name this outlet rootController and set the type to UITabBarController. Control-drag from the Tabs App Delegate to our Tab Bar Controller to connect the outlet.

Screen02

After that we return to Xcode in order to create this outlet in the TabsAppDelegate.h. Create a corresponding property as well (I assume you already know about Objective-C 2.0 Properties).

#import UIKit/UIKit.h

@interface TabsAppDelegate : NSObject 
{ 
  IBOutlet UITabBarController *rootController; 
  UIWindow *window; 
} 

@property (nonatomic, retain) IBOutlet UIWindow *window; 
@property (nonatomic, retain) IBOutlet UITabBarController *rootController; 

@end

Now, we will set and create two different controllers for the tabs: First, a subclass of UIViewController and secondly our long-awaited navigation controller ;) Head back to Xcode, control-click the Tabs project (the very first icon at the “Groups & Files” view) and choose Add > New File … Select an UIViewController subclass from the “Cocoa Touch Classes” section and name it FirstViewController (do not forget to create the .h file too). In addition to that create a FirstView.xib file in the Resources folder, open it in interface builder and build a simple view like the one below.

Screen03

In the Identity Inspector (+4) set the class of “File’s Owner” to FirstViewController and control-drag from “File’s Owner” to “View” to connect the view outlet. Save your work an switch back to MainWindow.xib. Now we have to tell the first tab that it has to care about our FirstViewController. In order to do so, select the first tab, open the Identity Inspector (+4) and set the tab’s class to FirstViewController. But what about it’s content? Switch to Attributes Inspector (+1) and choose FirstView from the NIB Name’s drop-drop down box. – What have we done here? We told the first tab to present the contents of our FirstView.xib. Finally, change the tab bar item’s title to “Simple View Controller”. If everything went well, the layout should look like this

Screen04

Time for our first launch, isn’t it? Almost. But first, we have to make some changes in our TabsAppDelegate.m

#import "TabsAppDelegate.h" 

@implementation TabsAppDelegate 

@synthesize window; 
@synthesize rootController; 

- (void)applicationDidFinishLaunching:(UIApplication *)application 
{ 
  // Override point for customization after application launch 
  [window addSubview:rootController.view]; 
  [window makeKeyAndVisible]; 
} 

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

@end

In [window addSubview:rootController.view] we add the view property of our root controller as subview to the window. Without that, our tab bar would never be seen on our device respectively our simulator. Save, build and run.

…drumroll…

Screen05

Woohoo! Everything went perfectly fine :) (If it did not for you, check your code and all outlets' connections). But now for the part you all waited for. Generally it’s almost the same steps but we have to watch out for some details. Create a new UIViewController subclass and name it “MyNavigationController” (don’t forget the .h file). Since we need an UINavigationController instead of an UIViewController, open MyNavigationController.h and change UIViewController to UINavigationController in the @interface directive.

#import UIKit/UIKit.h

@interface MyNavigationController : UINavigationController 
{ 

} 

@end

Since we will have to deal with the navigation controller programmatically to push and pop views, it’s a good idea to create an instance variable for it in our Tabs App Delegate class.

#import UIKit/UIKit.h
#import "MyNavigationController.h" 

@interface TabsAppDelegate : NSObject 
{ 
  IBOutlet UITabBarController *rootController; 
  MyNavigationController *navigationController; 
  UIWindow *window; 
} 

@property (nonatomic, retain) IBOutlet UIWindow *window; 
@property (nonatomic, retain) IBOutlet UITabBarController *rootController; 
@property (nonatomic, retain) MyNavigationController *navigationController; 

@end

Our navigation controller needs at least one view controller to display its contents. So go ahead, create another UIViewController named “SecondViewController” and also create a “SecondView.xib” with a layout similar to the first one.

Screen06

Save it and switch back to MainWindow.xib. Here comes the tricky part: Similar to the FirstViewController, select the second tab and set its class to MyNavigationController. After that, select the Tab Bar Controller and open the Attributes Inspector (+1). In the “View Controllers” section, change the term “Item 2” to “Navigation View Controller” (this will be displayed as the second tab’s title) and switch the class to NavigationController.

Screen07

Now we set the navigation controller’s root view controller: In the little MainWindow.xib Window expand the Tab Bar Controller. You should now see a Tab Bar icon and two sub controllers. Select the second one and expand it, too… Aaaaah, there it is! The navigation controller’s root view controller. Select it, set its class (+4) to SecondViewController and choose (+1) SecondView as NIB Name for the content. If everything went well, your layout should look like this:

Screen08

That’s it! Build, run and enjoy your creation

Screen09

As you can see, we built a sophisticated fully-fledged layout with less than 10 lines of self-written code. Feel free to play around with it or try to add some some more view controllers to the tab bar layout (it’s as simple as drag and drop).

Update

Due to huge demand I’ve added a second part of this tutorial that’ll walk you through the steps of having a table view controller instead of a normal view controller as second tab. Jump right into part 2

Filed under  //   development   iphone   tutorial  

About

Addicted to shiny apples, beautiful pixels, awesome code and electronic music.

TwitterFacebookFlickr