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
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

Good Job , let me ask you something , What i need to do for show an data infomation in another tabbar iten .
This has been a great help but do you mind if I ask how to add another controller to the tab bar so that you can return to the table listing without the use of the top nav bar i.e. something like
[self.navigationController popViewControllerAnimated:YES];
I’m not sure I understand your question. You want to add a tab that would pretty much act like a back button?
Brilliant! I have been wrestling with IB for nearly a week now, and I contend that it may seem easier, but creating this programmatically was definitely the way to go. Saying bye-bye to I-B.
Yes, exactly, just like a back button.
On another point, I haven’t found anywhere that explains that it’s possible to navigate to the next detail view without going back to the table view. In your example, te same as being in the “Dark Knight” detail view and going immediately back to “Batman Begins” or forward to “The Prestige”, again using a tab bar control. Any hints would be much appreciated!
Thanks for the quick response.
Ok, I haven’t done this so take this with a grain of salt. Since each tab is a view controller, you could just call your popViewController in the viewWillLoad method. That sounds kind of dirty though I’m sure there is a better way. Also, not that there is anything wrong with having a tab bar item acting like the back button but I’m pretty sure it’s against Apple UI guidelines. You wouldn’t win an Apple Design Award for this one
As far as navigating from one detail view to another from within the tab controller, you could add next and previous buttons somewhere on top of your detail view which would simply reload it with new data. You could add fade in/out or some other animations to let users know what’s going on.
Thanks again good to know it’s at least possible.
Yeah I agree, not my design just building for someone, so don’t have much choice. If I get it to work I’ll post in case anyone else wants to break the rules!
Hi , I don’t know if you can help me , I follow you in this post but i have a problem when i click in my tabbar, my first view works good and i can see my datainfo(plist), when i click in my second view i can see part of my data using textview.
let me know if you have time
Finally got the tabbar working as a back button, you just have to add this to the DetailViewTabBarController.m
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
// LIST being the name of my back tabbar item
if ([viewController.tabBarItem.title isEqualToString:@"LIST"])
{
[self.navigationController popViewControllerAnimated:YES];
}
}
and make sure you add [tabBarController setDelegate: self]; into the – (void)loadView {
of the tabbar controller in DetailViewTabBarController.m
ideally you also change this in DetailViewTabBarController.h
@interface DetailViewTabBarController : UIViewController {
to
@interface DetailViewTabBarController : UITabBarController {
hope this helps someone.
Thanks for the follow-up, Andrew! It still seems to me a bit an overkill to build up a view controller just to navigate backwards. Too bad there is no willSelectViewController method. Goes to show you UITabBarController wasn’t built for your use case
But I’m glad you got it to work anyway.
Hi , I receive this message when i try to run my aplication
:*** Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:
I don’t know what’s happend, i follow you step by step , today i can’t run..
thanks
Happy new year
Excellent tutorials. Is there any way you can show how to include the sections and index for the list. And perhaps a search bar? Thanks.
Hi Ed, these are excellent suggestions! I think a sections tutorial would be the next logical step. Thanks!
Vladimir – THANK YOU for your excellent tutorials. You explain things very clearly and I’m really looking forward to this DVD Library example application developing in future tutorials! Thanks again!
Very useful tutorial. Thanks very much!
Much of a christian bale fan ??
Hey,
Just want to say this is a killer bit of code that made my morning! The explanations are clear and to the point, and you don’t meander about with info that’s not useful… Perfect!
Thanks again,
Thom
Me again Vladimir,
Working through a project that uses your library setup, whenever I pop back to the main listing I have memory leaks. I’ve checked with your code and it happens there too when going back to the My DVD Library screen. I’ve tried a few options, if I release dao on didSelectRowAtIndexPath I have crashes when popping back to main listing as well as other issues. Any suggestions for modifications to your code to stop those leaks?
Thanks
Andrew
umm, figured out one way, if you release dao inside viewWillAppear that seems to fix the leak, this may not be best pratice……
- (void)viewWillAppear:(BOOL)animated {
[dao release]; /*HERE*/
dao = [[DvdLibraryDao alloc] initWithLibraryName:@”TestData”];
self.title = @”My DVD Library”;
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
}
Hi,
I have a question on this implemented code. I’ve been trying to work through it awhile and can’t quite figure it out.
So in my version of this I am pushing the Tab View in the exact same way as described here, however, each of my individual tab views are actually Table Views. This looks and displays great and works fantastic for sorting.
The problem I’ve run into is when I drill down off of that Table view, the Navbar gives me serious problems, it always stays on top, even when I pop Modal views. I’ve tried absolutely everything I can think of to hide the Navbar when drilling down but the Tab Bar’s Navbar always remains on top.
Using the example above, this would be like popping a modal view if you clicked on the Batman image. If you did this, the top of that popped modal view would be overlapped by the “Dark Night” navbar given in the example picture.
I have a feeling I need to call my Modal View pop ups from somewhere else, outside of the Tab Controller’s subviews. Anyone have any idea?
Thank you.
@Andrew: Great catch! That would actually make for a good blog post. There are two things that are going on there with the leaks here.
1. The dao object is never released
2. It’s initialized in the wrong place
The first one is easy to fix. Just add [dao release] in the dealloc method. The second one is kind of a neat mistake. Since viewWillAppear is called every time a view is displayed, we actually allocate dao more times than we release it. Putting the initialization in viewDidLoad is the way to go since that method is called only once when the view is initialized.
I updated the final source code to include the fixes.
@Thom: this requires a bit more elaborate response. Maybe try submitting it in the forums (http://iphonedevcentral.com/forum) and see the community can discuss it.
i followed your code as it is n it worked. tab bar is shown can load all viewcontrollers but viewDidAppear neither viewWillAppear gets called for any of view controller however viewDidLoad gets called.
where is the problem?
i have downloaded your project and in that also viewDidAppear neither viewWillAppear gets called for any of view controller however viewDidLoad gets called.
Hey,
Thanks for this tutorial! It made my work a lot more easier, because I had to create a tabbar programmatically, instead of using the IB. (i try to work without the IB because I absolutely despise it.)
Thanks again!
- B
Hello, great tutorial.
I know you published this tutorial long time ago, but I didn’t discover before now:)
Could you please show us how to include a UISearchBar in this project as well?
It would be great.
Thanks
Alex
Leave a Reply