iPhone app with tab bar and navigation bar as peers - iphone

I'm trying to write an application that uses a navigation bar and tab bar in what (I'm gathering) is an unusual manner.
Basically, I've got several "pages" ("home", "settings", etc) that each have their own tab. I'd also like to have it so that the "home" page is the root view of the navigation bar, and the other pages are the second-level views of the navigation bar. That is, I should be able to navigate to any page by clicking the appropriate tab bar item, and should be able to navigate to the home page from any other page by clicking the navigation bar's back button.
Currently, I have a UINavigationBar (through a UINavigationController) and a UITabBar (through a UITabController) as children of a UIView. The various pages' view controllers are set as the tab controller's viewControllers property, and the home page's controller is also set as the navigation controller's root view. Each page view's tag is set to its index in the tab control. I have the following logic in the tab controller's didSelectViewController delegate method:
- (void) tabBarController:(UITabBarController*) tabBarController
didSelectViewController:(UIViewController*) viewController
{
if ([navController.viewControllers count] > 1)
[navController popViewControllerAnimated:NO];
[navController pushViewController:viewController animated:YES];
}
Also, in the navigation controller's didShowViewController delegate method, I have the following code:
- (void) navigationController:(UINavigationController *) navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
tabController.selectedIndex = viewController.view.tag;
}
The problem that's occurring is that when I run this, the navigation bar, tab bar and home page all display ok, but the tab bar will not respond to input - I cannot select a different tab.
I gather it's more usual to have the tab bar as the child of the navigation control, or vice versa. This doesn't seem to fit my approach, because I don't want to have to individually create the subordinate control each time a change occurs in the parent control - eg: recreate tab bar each time the navigation bar changes.
Does anyone have suggestions as to what's wrong and how to fix it? I'm probably missing something obvious, but whatever it is I can't seem to find it. Thanks!
EDIT: I'm guessing it has something to do with both controller's trying to have ownership of the page controller, but I can't for the life of me figure out a way around it.

Yikes. You're way out on a limb here. I've learned the hard way that UINavigationController and UITabBarController aren't very accommodating when you try to use them in nonstandard ways. UINavigationBar and UITabBar weren't designed to coexist as siblings in the view hierarchy. They really want to be distant cousins. Maybe the approach you're pursuing can be made to work somehow, but I personally wouldn't even attempt it.
With that said, I'll suggest one thing you can check, and then I'll tell you how I'd approach the problem.
Have you implemented, and are you returning YES from tabBarController:shouldSelectViewController: in your UITabBarControllerDelegate? If not, that would prevent your tabs from switching.
Assuming that won't solve all your problems, here's what I would do to get the behavior you're seeking:
Create a UITabBarController and set its view as the root view of your window.
Have a separate UINavigationController under each tab, each with its own navigation bar. (This is the standard way to use UINavigationController and UITabBarController together.)
Set a UITableViewController that displays an identical copy of your main menu table as the root view controller for each navigation controller.
When a row is selected on your "Home" view controller, switch to the associated content tab.
Set a delegate on your UITabBarController. In its tabBarController:didSelectViewController method, call pushViewController:animated:. This is similar to what you're currently doing, except you'll be calling it on (UINavigationController *)tabBarController.selectedViewController.
In the viewDidAppear:animated: of the root view controllers of your content tabs, switch to the "Home" tab. This way, when the user hits the back button to go back to "Home", it will switch to the right tab. (You may also need conditional code to prevent this from happening when the user first selects a content tab. I'm not sure.)
I'll be curious to know if Apple accepts this nonstandard behavior. Come back and let us know. Also, let us know if you get your approach working somehow.

Related

View Hierarchy, UITabBarController, adding more views in certain tabs

So I'm not sure what is the best way to do something, or the preferred way. Here's essentially what we are trying to do:
-UITabBarController as the root
-first tab is a FruitViewController
-FruitViewController can push an AppleViewController (not sure if push is the correct word, but basically go to the next viewController)
-AppleViewController has a back button in the UIToolBar to return to FruitViewController
-second tab is VegetableViewController
-VegetableFruitViewController can push CeleryViewController
At the end of both ViewControllers, you can push another ViewController called CalorieViewController. My question is that what's the best way to present view controllers per tab? Since I want both tabs to use CalorieViewController, in order to reuse it, I'm assuming I need to keep it non-coupled with the other ViewControllers.
The way my coworker implemented is, on each tab, all ViewControllers for that tab are created and added as a subview. Based on the button pressed, it hides and shows the other view. This seems like not a good idea. It seems like presentModalViewController, and dismissModalViewController (for the back button) might work, but I'm not sure if that's the preferred method.
I would suggest you make the root view controller for each tab a UINavigationController, with the view controller's that are currently set for the tabs as the root view controller of the navigation controller. That way you can push and pop views all you want. UIViewController even has support for automatically pushing the tab bar off the screen when pushing a new view controller onto the stack.

Changing viewcontroller for a tab in a tabbar controller

I am currently developing an app that has a TabBarController and each of the tabs contains a navigation controller. This way on each tab I can show details of the rows selected on a view by pushing the viewcontroller to the navigation controller. Each of the views also have an UINavigationItem above them. In this navigation item I placed a button.
But now I would like to change the viewcontroller for a certain tab, when clicking the button in the UINavigationItem, BUT the view(controller) I want to change to has to act like the root view controller of that tab.
So I do not want to push another view on the navigation controller, but just switch to that view (in the same tab) and have that act as the root view controller.
I cannot find a good way to do this, with actually having the views work correctly. They either do not dealloc when I switch views (which would be nice, because I want to keep memory usage to a minimum).
One way of solving this, might be that I add more tabs to my TabBar Controller and just switch to the right tabs when I click the button, but that would be a last resort.
Not really sure if I described this correctly, but I was wondering what the best way is to do this. My preference is having 3 viewcontrollers and switch between them.
Hopefully I understand your question correctly: you want to basically 'reset' your navigation controller to have a new root.
You can do this by telling your navigation controller that you want to display a new set of view controllers:
[navigationController setViewControllers:[NSArray arrayWithObject:newViewController]
animated:NO];
This will get rid of all view controllers currently on that navigation controller's stack, and reset the root view to newViewController.

One UINavigationcontroller for the whole app?

i´ve created a UINavigationController in my appdelegate and initialized it with my "modelselectionViewController". This VC has different uibuttons and when touched, a new VC ("modelViewController") is pushed on the navigationstack.
This "modelViewController" acts as my template view and has a uitabbarcontroller with different tabs. The first VC is shown immediately but any changes on the navigationcontroller doesn´t work. I would like to set the name of the title but that navigationcontroller is null.
NSLog(#"navi: %#",
self.navigationController);
If i change my code to push the different VC when touching the different tabs, navigation works but only with a third level of navigation hierachy.
I want to know if it´s possible to use only one navigationcontroller for all my different tabs. Hope i made my setup clear. Appreciate all your help. thanks
I think you might want to read Combining ViewControllers.
In general, you should have the tabbar controller as a 'root' controller, not as a 'child' controller. A quick search in Apple's doc didn't yield a formal 'forbidden', but it might be.
If you create a UITabBarController from a view that's managed in a UINavigationController (ie: if you create a navigationcontroller first, and it's still around when you create the tabbarcontroller), you're starting a fight with the frameworks. Here's the admonishment from the docs on combining viewcontroller interfaces:
An application that uses a tab bar
controller can also use navigation
controllers in one or more tabs. When
combining these two types of view
controller in the same user interface,
the tab bar controller always acts as
the wrapper for the navigation
controllers. You never want to push a
tab bar controller onto the navigation
stack of a navigation controller.
Doing so creates an unusual situation
whereby the tab bar appears only while
a specific view controller is at the
top of the navigation stack. Tab bars
are designed to be persistent, and so
this transient approach can be
confusing to users.
I read that as "if it doesn't break something that we haven't thought of on the next update, we might reject the app anyway because it's 'confusing to users.'"
I suppose you could kill the whole navigation hierarchy and the navigationcontroller if you don't need to return there (like if you just used it for a one-time setup screen). Or you can look into other options for navigating within a viewcontroller that's managed by the navigationcontroller.
One thing to try might be to navigate to a UITableView, and use its cells to push a modal view onto the navigation stack. That would be familiar to users and also jive with the intent of the navigation classes.

What is the pattern for an Iphone app with both a View Controller and Tab Bar Controller?

I am trying to write an iPhone app that has both a UITabBar controller (and its associated views) and a plain vanilla view controller that is not part of the TabBar (i.e. an initial config page that only gets displayed the first time the app is run).
I am able to put a Tab Bar Controller and a View Controller in MainWindow.xib and shuffle between the two in the app delegate.
While this works I'm wondering if this is the best way to be implementing this.
It doesn't feel very "MVC-ish" to me but I think the two different controllers both need to be root (?)
I don't know how else I would do it.
If the config page is really only a "run once" affair, you could just pop it as a modal view from within the tab bar controller via the presentModalViewController:animated: method. (If on the other hand the config page is ever likely to be required in the future, I'd just add it as another option on the UITabBar.)
You would make the tabbarcontroller the default view. And present the viewcontroller modally in viewWillAppear or similar method. Then when you want to switch to the tabbar, you'd dismiss the modal view controller.

Toolbar items in sub-nib

This question has probably been asked before, but my google-fu must be inferior to everybody else's, cause I can't figure this out.
I'm playing around with the iPhone SDK, and I'm building a concept app I've been thinking about. If we have a look at the skeleton generated with a navigation based app, the MainWindow.xib contains a navigation controller, and within that a root-view controller (and a navigation bar and toolbar if you play around with it a little). The root-view controller has the RootViewController-nib associated with it, which loads the table-view.
So far so good. To add content to the tool bar and to the navigation bar, I'm supposed to add those to in the hierarchy below the Root View Controller (which works, no problem). However, what I can't figure out is, this is all still within the MainWindow.xib (or, at runtime, nib). How would I define a xib in order for it to pick up tool bar items from that?
I want to do (the equivalent of, just reusing the name here)
RootViewController *controller = [[RootViewController alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:controller animated:YES];
[controller release];
and have the navigation controller pick-up on the tool bar items defined in that nib. The logical place to put it would be in the hierarchy under File's Owner (which is of type RootViewController), but it doesn't appear to be possible.
Currently, I'm assigning these (navigationItem and toolbarItems) manually in the viewDidLoad method, or define them in the MainWindow.xib directly to be loaded when the app initializes.
Any ideas?
Edit
I guess I'll try to explain with a picture. This is the Interface Builder of the main window, pretty much as it comes out of the wizard to create a navigation based project. I've added a toolbar item for clarity though. You can see the navigation controller, with a toolbar and a navigation bar, and the root view controller.
IB Screenshot http://img526.imageshack.us/img526/3507/rootviewcontroller.png
Basically, the Root View Controller has a bar button item and a navigation item as you can see. The thing is, it's also got a nib associated with it, which, when loaded will instantiate a view, and assign it to the view outlet of the controller (which in that nib is File's Owner, of type RootViewController, as should be).
How can I get the toolbar item, and the navigation item, into the other nib, the RootViewController.nib so I can remove them here. The RootViewController.nib adds everything else to the Root View Controller, why not these items?
The background for this is that I want to simply instantiate RootViewController, initialize it with its own nib (i.e. initWithNibName:nil shown above), and push it onto the navigation controller, without having to add the navigation/toolbar items in coding (as I do it now).
First off, the way you've worded your question is a bit confusing here, but I think I understand.
You've got a couple options here. If you want to design your toolbar, just add one to the view you are loading and drag the various buttons and separators onto it in Interface Builder. Then create IBActions in that view's view controller and drag connections from your toolbar items to the various actions. Since you are using a Navigation controller, though, you will want to hide the toolbar you get by default by unchecking the "Shows Toolbar" box in your MainWindow.xib when the UINavigationController is selected (Attributes tab in the IB inspector).
alt text http://cimgf.com/files/toolbarthing.png
Alternatively, you can just add toolbar items programmatically in the viewDidLoad method by calling [self setToolbarItems:items]; where items is an NSArray of toolbar items.
Really there is no way that I'm aware of to cause the navigation controller to use your custom toolbar. Just hide the main one and use your custom one.