XCode4: UITabBarController with UINavigationController using Interface Builder

If this helped you, please consider downloading my FREE iPhone app "Hydrate Yourself"and leaving a review.


There are a whole bunch of tutorials to create a Navigation Controller inside a TabBarController with XCode3.  But there are a number of small changes in XCode4 which can trip up someone not familiar with them.  So this guide is essentially an updated version of those other guides, designed to create the same simple demo using the new tool.

1. Create a new Project
We are going to start with a Tab Bar Application, so go ahead and create a new project as usual. I'm calling mine Navigation Tab Bar.

1. Create new project

2. Select the Tab Bar
Click MainWindow.xib and you should see the integrated Interface Builder. On the left is a section called Objects, select the Tab Bar Controller.

2. Select Tab Bar

3. Add the Navigation Controller
On the right side of your screen click the Utilities box. This is new in XCode4 and should look familiar if you've been using XCode3. At the bottom find the Navigation Controller and drag it into the Tab Bar in the middle of your screen.

3. Expand Utility View and Add Navigation Controller


3b. Navigation Controller Added
Here you can see I added it as the middle Tab
4. Configure new tab
Back in the Objects section you can now expand the Navigation Controller and select the View Controller object. Now on the right side you can select the Identity Inspector at the top and put in your UIViewController's class name. For the purpose of this demo I am calling it "YourNewViewController" which we will make at the end.

4. Expand new Navigation Controller select identity inspector and fill in new View Controller name

Once that is done switch to the Attribute Inspector and put in the name of your XIB file in the NIB Name section. Note: do not add the .xib extension, this will cause a runtime crash.

4b. Switch to Attribute Inspector and Fill in the nib name

5. Create the new UI View Controller
This is probably familiar to you already, create file from the File menu and select a UIViewController subclass.

5. Create new UIViewController


5b. With XIB
Be sure to select With XIB for user interface


5c. Use the same file name as before
Use the same file name as you used before.

6. Add an IBAction
We will be adding a button to push a view onto the navigation controller.  Open up the header file and add an action.

8. Add an IBAction for our button
I just added the buttonPushView line

Switch to the implementation file and add another few lines for a simple call to pushViewController.

9. Tell the navigationController to push a new View Controller
This is the simplest pushViewController line I could come up with

7. Add a button
Select YourNewViewController.xib in the file menu. Adding a button is a simple matter of dragging/dropping from the bottom right Objects section.

Next you link the button to the new IBAction by right clicking the button and dragging the arrow to File's Owner, in the Placeholders section. Select buttonPushView.

10. In the new XIB file drag in a button and link to the IBAction

8. You're done!
All thats left now is the build and run (Command-R).  Select the second tab to see your new Navigation Controller and click the button to see it in action.

8. Build and run the simulator. Press the button in the second bar item to try it out      8b. It works

27 comments:

Anonymous said...

Thank you so much!

Very helpful post!

XCode4 very trouble for me after xcode3 :)

P.S. Sorry,my english so bad.i'm russian :)

Good luck!

Anonymous said...

Thanks so much! I was stuck because I was changing the class of the pre-made view controller to UINavigationController instead of dragging in a new one, which didn't expose the ViewController attribute inside it.

Anonymous said...

Thank you! I spent hours trying to figure out how to do this using XCode 3 tutorials (some great ones to, but ones that didn't work in XCode 4). I can't believe the solution was a drag-and-drop 10 minute solution. Thanks a ton for sharing this!!

Anonymous said...

Thank you very much! I have been struggling with this for a few hours and you saved me a lot of time with this!

Anonymous said...

Thanks a lot. This helps, but I'm running into a problem. If I want to load a UITableView instead of a UIView, what would I need to change?

Will Winder said...

In step 7 instead of adding a button you could add a UITableView. You'll have to setup the tables data source and delegate outlets and your view controller class will need to conform to the UITableViewDelegate and UITableViewDataSource protocols.

I wrote about this last year when I was first figuring it all out, maybe this will help:
http://blog.willwinder.com/2010/10/uitableview-which-is-its-own-delegate.html

Anonymous said...

I tried to set the title of "Root View Controller" using [self setTitle:@"My Title"], but not success. Thanks a lot.

Will Winder said...

Try setting the title in viewDidLoad or viewWillAppear, it is likely your outlets haven't been setup yet.

Anonymous said...

First: Great Tutorial! :-)
Second: (and perhaps a beginner's question) - the view that you're pushing onto the stack when you're clicking the button - that's a non-existant view, right? I mean you're alloc-ing and init-ing it on the fly, which is why its a blank white screen, correct?

My question is this then: what's the syntax for pushing a view that does exist? Like if you want an actual view that you've created and designed (with a .xib file) to be pushed onto the stack - how would you write that?

Cause I created a new UIViewController called "SomeRandomScreen" (giving me the .h , .m , and .xib files.) Then I tried:


-(IBAction) buttonToPushView:(id)sender {
UIViewController *targetScreen = [[UIViewController alloc] initWithNibName: @"SomeRandomScreen" bundle:[NSBundle mainBundle]];

[self.navigationController pushViewController: targetScreen];
}



...and CRASH ! It all came tumbling down :-(

Any ideas?

Will Winder said...

I wouldn't call it a "non-existant" view. It is a real view, it just has nothing on it.

When I'm creating a UI programatically, I'll frequently do a simple modification like changing the background to make it easier to see what the heck I'm doing: "targetScreen.view.backgroundColor = [UIColor greenColor];"

That said, the code you posted should work fine. Without seeing the error message when it crashes I can't really say whats wrong with it.

There are a few small steps you can take to figure out what you're doing wrong:

1) Try allocating the empty UIViewController and pushing it onto the navigation controller, but change the background color.

2) Once that works, try pushing the UIViewController I made in the tutorial onto the stack just like you're doing, but with "YourNewViewController". You should be able to push it onto the stack over and over again.

3) Once you get all that working you can try getting your "SomeRandomScreen" loading. Look closely at step 5-7 in the tutorial, since they do exactly what you want to do as far as creating a UIViewController from a NIB.

Anonymous said...

ok that worked - but here's the REAL question :-)
What if I wanted the Navigation Controller that I'm creating to then contain a TableView? But I don't want it be from a UITableViewController, just a regular Table-View?
Let me be more specific:
1) we have our FirstViewController, SecondViewController and of course the new View-Controller we created as per your instructions called MyNewViewController.
2) Because I wanted MyNewViewController to load a view that has a NIB (xib) file, I also created yet another view called "peachScreen", which is a subclass UIViewController, which then gave me a peachScreen.h, peachScreen.m, and peachScreen.xib files (and of course I made the background color of the xib file be peach.)
3) Finally, I set this up: MyNewViewController has a button which push-loads the peachScreen view.

All this works just fine.

BUT, if I wanted to now put a table inside of peachScreen - meaning I want to drag a Table-View from the Library/Objects panel (not a Table View Controller! just a Table View) and drop it in the middle of my peachScreen, and resize it so that its like only half the page, and then have the individual cells of that table drill-down and take me to other detail-views - without ever losing my TabBar at the bottom - well how the HECK do I do that? :-)
Cause that's what's killing me. This is where I get lost with trying to figure out what controls what. I get crashes and errors - maybe I'm just not connecting things correctly with File's Owner - but I think I'm setting things up right, I'm making sure to include the and - but I'm crashing all over the place.
Any tips (or even code) on setting up this "engine" so that it works correctly?

Anonymous said...

Thank you! I I spent hours trying to figure out how to do this using XCode 4 and in the end you blog save me

Best regards

Anonymous said...

Just doesnt work for me. the button loads and when I push it nothing. I been looking forever for something like this.

Anonymous said...

thanks , it is very hopeful.

Technology Blog said...

OMG Thank YOU! I had to rewrite code from iOS 5 to iOS 4! You saved me!

Anonymous said...

It´s not working man! I get Thread 1: Program recieved signal "SIGABRT"
What is that? Why is´nt work?

Andrew said...

This is great! I have been struggling with this for two weeks! Thank you very much for posting it.

I do have two questions. I am making an app that has 4 tabs, two of which I want to have Navigation Controllers. So I repeated the steps to put a second Nav Controller, having it load a different class and NIB file, but this time I'm getting two errors. A MainWIndow.xib unsupported configuration and an error in my new AboutViewController.m on a line of code I didn't edit; something about UINavigation Controller may not respond to pushViewController animated. but the error shows up in the code below.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}

Also, can you detail how we then work with the views we are pusing onto the stack?

Thanks!

Andrew
PeakMobileDesigns@gmail.com

Anonymous said...

Thanks so much for your tutorial !

But I just want to know if it's possible having only 2 tabs.

I already have 2 tabs UIViewController and the first one contains a UITableView then I want to display a view with the detail for that row.

I understood the way you emplemented the UINavigation Controller but I don't how to remove the new TabBar Button.

Thanks
Ray

Anonymous said...

Thanks. Simple by great tutorial.

Anonymous said...

I have following your tutorial step by step. Everything is OK except: when I press the button, it gives exception:

'-[UIViewController buttonPushView:]: unrecognized selector sent to instance 0x4e2c2b0'

I really in trouble with it.

Will Winder said...

The unrecognized selector probably means you set the class name in the wrong spot during step 4.

Anonymous said...

Thank you... this saved me a lot of time

Anonymous said...

I'm trying to make the combined tab and navigation application respond to rotation of the device, but without luck. What I have done is to always return YES in - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
in all views.

Any ideas on how to to get this working?

Anonymous said...

I've been programming for over 20 years but am a total beginner with Xcode.

Perhaps the Xcode 4.2 interface has changed since version 4.
The sequence of dialogs in 4.2 looks completely different to the tutorial above. Perhaps my settings are not correct. The create project dialog did not create a mainWindow.xib so I created one myself. I did not see anything called Integrated Interface Builder. I could not arrange a combination of objects to match the Object navigator and the auto generated code in 4.2 is significantly different to the above example.

If found the Introductory tutorials in the Xcode documentation also do not match what I see on the screen. Maybe getting too old for this :-)

Paul

Macbook Pro
Version: 4.2 (4D199)
Xcode: 4.2 (828)
Instruments: 4.2 (4233)
Dashcode: 3.0.2 (336)
SDKs:
Mac OS X:
10.6: (10K549)
10.7: (11C63)
iPhone OS: 5.0: (9A334)
iPhone Simulator: 5.0: (9A334)

Will Winder said...

Hi Paul,

Some of my comments in my directions were aimed at people who were upgrading from Xcode3 so that is probably adding some confusion.

I just double checked using version of 4.2 and it looks like things are pretty much the same (I may have been using that version all along). Some of the windows you need to use are collapsed by default but I mention how to open them, most of the code examples I put in my instructions are not auto generated. When I said "Integrated Interface Builder" I meant it is now part of Xcode, it used to be a separate program. Just click on the file ending in .xib and it automatically opens.

Make sure you select "Tab Bar Application" when making your new project, the MainWindow.xib file should definitely be there. The rest of the steps should make more sense after you have that. There are all sorts of config files hidden away to do things like pick which window launches with your program and also some special considerations which you need to do for the "main" window. I think its best (for your sanity at least) to leave the defaults as much as you can until you absolutely need to change it.

Another thing I felt was useful when first starting was to create my UI's programmatically. For me the API's were much more intuitive than the GUI's. Eventually I started to embrace the GUI as a time saver, especially for things like this.

Stick with it, iOS development seems strange for a while but eventually you'll start to see a method to the madness.

Anonymous said...

Hi Will,

The last commenter was correct. For new tab bar applications created using the templates that come with Xcode 4.2.1, the template no longer creates a MainWindow.xib.

Other than that, great explanation - thanks.

華宅 said...

this is very helpful! thank you

Post a Comment