Create a UITabBarController from scratch

Tab bar based apps are probably as common as table-based apps and it’s even more common to see them combined. That’s what we’re going to do in this tutorial.

There is a very easy way of creating a tab bar application. In fact, it’s so easy it requires no work whatsoever. When you choose to create a new iPhone application in Xcode, one of the options is Tab Bar Application. Just the bare-bones template provided by Apple gives you a fully-functioning app with two tabs. That is not what this tutorial will be about. We are going to create a tab bar controller programatically. It’s really a lot easier than most people think.

Prerequisites

This tutorial is a continuation of my previous tutorials so if you haven’t followed them, it’s a good time to catch up.

We’re going to be starting off from a slightly modified version of the previous source code. You can download the primer code for this tutorial here: MyDVDLibrary04Primer. The only changes made were some name changes (DetailViewController became DvdInfoViewController) and I organized all classes into groups.

Also, I’ve recently upgraded to OS X Snow Leopard + Xcode 3.2 with iPhone SDK 3.0 (not 3.1 yet) so the project was compiled on that platform.

1. Create DetailViewTabBarController class

We’re going to add a view controller that will create a tab bar controller with three tabs: Info, Stats and Borrowers. Each of the tabs will be a separate, self-contained view controller. We already created one of them, DvdInfoViewController, which was previously called DetailViewController. The reason I renamed it is that the real detail view controller will now be our tab bar controller.

In Xcode, right-click on the View Controllers folder from the the Groups & Files panel on the left and choose Add -> New File. Choose UIViewController subclass from the Cocoa Touch Class section and click Next. Name the file DetailViewTabBarController.

Note: You may be wondering why we didn’t choose a UITabBarController subclass instead. As you can see that option wasn’t even offered. This is because UITabBarController is not meant to be subclassed. Instead, we create a generic view controller to which a tab bar controller will be assigned.

2. Create DvdStatsViewController and DvdBorrowersViewController

These are the new view controllers I mentioned previously. We already have our DvdInfoViewController that will be in the first tab, so let’s create the remaining two.

In Xcode, right-click on the View Controllers folder and choose Add -> New File. Choose UIViewController subclass and name the file DvdStatsViewController.

To create DvdBorrowersViewController do the same thing, only name it DvdBorrowersViewController.

That’s it about those two controllers for now. I’m not going to cover actually filling them with any content since that’s not the point of this tutorial. We will leave them here as placeholders for future tutorials when we cover Core Data and RSS feed processing.

3. Set up DetailViewTabBarController

Since we’re going to be coming to the tab bar controller from the RootViewController, we need to pass it DVD info from the row that was tapped. We can do that in a custom init method in DetailViewTabBarController. You’ll see how it’s used later when we instantiate it in didSelectRowAtIndexPath method of RootViewController.

We’re also going to need a handle on the data about the DVD that was tapped on the front page.

Declare it all in DetailViewTabBarController.h

@interface DetailViewTabBarController : UIViewController {
    NSDictionary *dvdData;
}
-(id)initWithDvdData:(NSDictionary *)data;
@end

Next, let’s define initWithDvdData in DetailViewTabBarController.m

-(id)initWithDvdData:(NSDictionary *)data {
    if (self = [super init]) {
        dvdData = data;
    }
    return self;
}

All we’re doing here is setting local instance variable dvdData to whatever is passed in. We now have a handle on the DVD data and can use it across all the view controllers.

4. Implement loadView method in DetailViewTabBarController

loadView is a method on UIViewController that can be overridden in case you want to build your view from ground up. That’s exactly what we want to do so let’s override loadView now. Let’s discuss it piece by piece;

- (void)loadView {
    UIView *contentView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
	contentView.backgroundColor = [UIColor whiteColor];
	self.view = contentView;
	[contentView release];

Borrowing the biblical line; in the beginning there was nothing, we need a base view to build upon. So we created an empty view with a white background that takes up the whole screen. We will attach all the other views to it.

Now we need to instantiate all three of our controllers. Each one will become one tab in the tab view.

    // Declare all three view controllers
    DvdInfoViewController *dvdInfoController = [[DvdInfoViewController alloc]
                                                initWithDvdData:dvdData
                                                nibName:@"DetailViewController" bundle:[NSBundle mainBundle]];
    DvdStatsViewController *dvdStatsViewController = [[DvdStatsViewController alloc] init];
    DvdBorrowersViewController *dvdBorrowersViewController = [[DvdBorrowersViewController alloc] init];
 
    // Set a title for each view controller. These will also be names of each tab
    dvdInfoController.title = @"Info";
    dvdStatsViewController.title = @"Stats";
    dvdBorrowersViewController.title = @"Borrowers";

You see the dvdInfoController is initialized the same way we did it before in RootViewController. We pass it dvdData that we get from our custom initialization method.

Now the meat of this method, let’s create the actual tab bar controller and give it the three view controllers. You can note that we’re setting the bounding frame to be 320 pixels wide and 460 pixels high. We shave off 20 pixels because the title bar is already taking up, well, 20 pixels.

UITabBarController creates tabs when you call setViewControllers on it and pass it an array with your controllers in it. That’s what’s happening here.

    // Create an empty tab controller and set it to fill the screen minus the top title bar
    UITabBarController *tabBarController = [[UITabBarController alloc] init];
	tabBarController.view.frame = CGRectMake(0, 0, 320, 460);
 
    // Set each tab to show an appropriate view controller
    [tabBarController setViewControllers:
     [NSArray arrayWithObjects:dvdInfoController, dvdStatsViewController, dvdBorrowersViewController, nil]];

And finally, let’s clean up objects we no longer need from memory and add the tab controller view to our parent view so we can actually see it.

    // Clean up objects we don't need anymore
    [dvdInfoController release];
    [dvdStatsViewController release];
    [dvdBorrowersViewController release];
 
    // Finally, add the tab controller view to the parent view
    [self.view addSubview:tabBarController.view];
}

5. Modify RootViewController’s didSelectRowAtIndexPath method to display DetailViewTabBarController

The last step we need to take is to modify didSelectRowAtIndexPath in RootViewController to display our newly created tab bar controller instead of the DvdInfoViewController we showed in the last tutorial.

Open up RootViewController.m and modify didSelectRowAtIndexPath to look like this:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    DetailViewTabBarController *controller = [[DetailViewTabBarController alloc]
                                              initWithDvdData:[dao libraryItemAtIndex:indexPath.row]];
    controller.title = [[dao libraryItemAtIndex:indexPath.row] valueForKey:@"title"];
    [self.navigationController pushViewController:controller animated:YES];
    [controller release];
}

At this point, you can run your project and tapping on a DVD title should take you to the tab bar controller which has three tabs. The first tab contains the movie info, the remaining two are empty. As I mentioned before, we’ll fill those in in the later tutorials or it can be left as an exercise for the reader.

6. Add icon to tabs

The tabs now only have a textual title. Let’s make it prettier by adding an icon to each of them. I only use one image (included in the project) for demonstration purposes but you can have a different image for each tab. The image should be 32×32 pixels PNG file. You don’t need to to create the pretty, shiny, gray and blue images. Just pass it a normal image and the SDK will do the rest.

Add this in the loadView method in DetailViewTabBarViewController.m implementation file right after we set the titles of the controllers:

    // Set a title for each view controller. These will also be names of each tab
    dvdInfoController.title = @"Info";
    dvdStatsViewController.title = @"Stats";
    dvdBorrowersViewController.title = @"Borrowers";
 
    dvdInfoController.tabBarItem.image = [UIImage imageNamed:@"dvdicon.png"];
    dvdStatsViewController.tabBarItem.image = [UIImage imageNamed:@"dvdicon.png"];
    dvdBorrowersViewController.tabBarItem.image = [UIImage imageNamed:@"dvdicon.png"];

If everything went well, you should see your app looking like this

tutorial020

Conclusion

While it may seem easier to create everything in the Interface Builder, you can see that in this case creating a tab bar controller was a breeze. Adding new tabs/view controllers to it is as easy as adding an object to an array. I hope this tutorial connecting the dots how to go from a table view to a tab bar view in a navigation-based application.

You can download the complete source code to this tutorial here: My DVD Library Tutorial 4


31 Responses to “Create a UITabBarController from scratch”

Copyright © 2009 Vladimir Olexa | Copyright © 2009 Apple Inc. | Powered by WordPress