views:

683

answers:

2

Hey everyone, I am new to iPhone development and I'm not understanding the whole UINavigationController and UITabBarController idea. Is one a substitute for the other - how do apps such as Tweetie combine both?

I'd like to have my app have a persistent Tab Bar @ the bottom (which seems to be working), but also a Navigation bar at the top which can push/pop views onto the screen without removing the tab bar.

  • How can I accomplish this?
  • What should the hierarchy look like in IB as far as my MainWindow.xib with regards to all of these controllers?
  • What is best practice here?

Thanks very much,

A: 

Use the wizard for a Tab Bar Application, and set it up as normal. In any tab where you want to add a navigation controller, create it in the XIB using the library. My XIB has:

- File's Owner          DescriptiveNameNavViewController
- First Responder
- View                  UIVIew
- Navigation Controller UINavigationController
  - Navigation Bar      UINavigationBar

Note that there isn't anything in the view. See viewDidLoad below for where the UINavigationController gets attached to the UIView.

In the header file for the Tab's ViewController (which I've here called DescriptiveNameNavViewController -- there isn't a particular standard for this, but I use [Something]NavViewController to remind me that this ViewController contains a navigation controller with the navigation stack. This is the controller name that I set in the MainWindow.xib that the wizard generates) Set up a UINavigationController * IBOutlet that has the navigation controller in the XIB attached to it:

@interface DescriptiveNameNavViewController : UIViewController {
    UINavigationController *navigationController;
}
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

@end

In the controller for the DescriptiveNameNavViewController , do something like this:

- (void)viewDidLoad {
    [super viewDidLoad];
    [[self view] addSubview:[navigationController view]];
    DescriptiveNameController *aController = [[[DescriptiveNameController alloc ] initWithNibName:@"DescriptiveNameController" bundle:nil ] autorelease];
    aController.title = @"Descriptive Title";

//
//  app stuff goes here.
//

    [self.navigationController pushViewController:aController animated:YES];
    [self.navigationController setDelegate:self];
}

Setting the delegate in the DescriptiveNameNavViewController is super-important, because otherwise you won't get the methods called that you expect in DescriptiveNameViewController instances and anything else you push into the navigation controller's stack.

In DescriptiveNameNavViewController, implement the UINavigationControllerDelegate methods like this:

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if ([viewController respondsToSelector:@selector(viewDidAppear:)]) {
        [viewController viewDidAppear:animated];
    }
}

And that will cause messages to get propagated to controllers inside the UINavigationController like you expect. It seems like many problems that people encounter are because the viewDidAppear: or other methods aren't getting called on the ViewControllers pushed into the NavigationController.

Anyway, let me know if more detail would help.

corprew
I have followed this and it doesnt work. Still not getting this at a conceptual level. When I launch the app and click on the second tab, it simply loads the view, and not one with a navigation controller.Also, you are missing the UINavigationControllerDelegate protocol in your header. Why isnt navigationController defined as an IBOutlet in the interface, but IS in the property statement?
barfoon
There's only an IBOutlet in the @property statement because that's where it gets picked up from by interface builder. It's an empty macro.If you're getting an empty view on the second panel, it's because loading the Navigation Controller in viewDidLoad isn't working. At any rate, if chrissr's solution is working for you, just watch for the viewWill* and viewDid* methods not getting propagated to your subviews and if so implement the delegate. (The missing delegate protocol was a mispaste, but the methods would get called anyway.)
corprew
+2  A: 

Just wrap the view controller inside the Navigationcontroller and Place the NAvigationcontroller insided the Tabbar. This will work fine for you…

Ex:

NSMutableArray *tabBarViewControllers = [[NSMutableArray alloc] initWithCapacity:2];

tabBarController = [[UITabBarController alloc] init];
[tabBarController setDelegate:self];

UINavigationController *navigationController = nil;
navigationController = [[UINavigationController alloc] initWithRootViewController:<Your View controller1>];
[tabBarViewControllers addObject:navigationController];
[navigationController release];
navigationController = nil;

navigationController = [[UINavigationController alloc] initWithRootViewController:<Your View controller2>];
[tabBarViewControllers addObject:navigationController];
[navigationController release];
navigationController = nil;

tabBarController = tabBarViewControllers;
[tabBarViewControllers release];
tabBarViewControllers = nil;
Manjunath
This makes much more sense, each view controller can have their own Navigation controller to pop/push views on. Thank you!
barfoon