TabBarController in a NavigationConroller - ios

I have a UITabBarController within a UINavigationController, I know the iOS documentation for the UINavigationController says the following:
rootViewController
The view controller that resides at the bottom of the navigation stack. This object cannot be an instance of the UITabBarController class.
So does this mean that if I have UIViewControllers already on the navigation stack, it's ok then to push a UITabBarController, once it's not the root item?
I have this at the moment and all seems ok except when I pop the UITabBarController, the dealloc or viewDidUnload isn't called in any of the TabBarItems ViewController, do I need to do something similiar for getting the viewWillAppear to work?
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
[viewController viewWillAppear:NO];
}
Thanks

I am not sure that having a UITabBarController within a UINavigationController is going to work.
I usually do this the other way round
companyNavController = [[[UINavigationController alloc] initWithRootViewController:companyViewController] autorelease];
companyNavController.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemSearch tag:1];
[tabBarController setViewControllers:[NSArray arrayWithObjects:phoneBookNavController, companyNavController, faveNavController, settingsNavController, nil]];
If you'd like to hide the TabBar part of your App, you could always try hidesBottomBarWhenPushed to manage this.
HTH

Related

UIToolbar and UINavigationBar not hiding on setHidden:YES

I'm using storyboards and I've referenced the storyboard and I'm getting a value back using my Storyboard ID.
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UINavigationController *navigationController = (UINavigationController *)[mainStoryboard instantiateViewControllerWithIdentifier:#"NavigationController"];
This is done within the UIViewController -viewDidLoad: method that's currently at the root of the UINavigationController hierarchy.
In the storyboard I've chosen to show both the UINavigationBar and UIToolbar associated with UINavigationController. I then set them to hidden in my code for the hell of it:
UINavigationBar *navigationBar = navigationController.navigationBar;
[navigationBar setHidden:YES];
UIToolbar *toolbar = navigationController.toolbar;
[toolbar setHidden:YES];
but they're still visible! Why the heck are they still visible? Stepping through the code tells me that these variables have a non-nil value. Furthermore, I would obviously get a run-time error if they were nil.
I have also tried the methods on UINavigationController:
[navigationController setNavigationBarHidden:YES];
[navigationController setToolbarHidden:YES];
These don't work either.
I would like to note that the program doesn't crash. It just glides over these methods with no warnings. Checking the state of these "bars" indicates that they are in fact hidden (using isNavigationBarHidden and isToolbarHidden on UINavigationController instance) but this isn't apparent in the simulator.
edit:
problem was instancing the navigationController and assigning it to a local variable instead of accessing it through the embed navigation controller property of a UIViewController
Problem solved

Instantiate UINavigationController on button action

I have simple application with only one main view, which has 'Settings' button, and settings are tree-grouped, so I wand to present them in navigation controller. And I don't want navigationController in main view, because I don't want navigation bar there.
That's why I don't instantiate navigationController in application: didFinishLaunchingWithOptions:. And when I check self.navigationController in 'Settings' button handler, it returns nil.
So I wrote this: (I use ARC)
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[self.view.window setRootViewController:navigationController];
[navigationController pushViewController:settingsViewController animated:YES];
}
This works, although it pushes settingsViewController without animation (don't know why).
Is this generally the correct way to do - to change rootViewController in the middle of running app?
And if yes - than when I'm done with Settings, I probably need to set rootViewController back to current viewController, as it was before I tapped 'Settings'?
I think you want to create a navigation controller that you will present modally; the following will do:
SettingsViewController* settingsViewController = [[SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] init];
[navigationController pushViewController:settingsViewController animated:YES];
[self presentViewController: navigationController animated: YES completion:nil];
where self here is the view controller you want to trigger the modal view controller from.
since you present modally the navigation controller you can dismiss it within the code source of your settingsViewController by accessing its navigation controller:
[self.navigationController dismissViewControllerAnimated:YES completion: nil];
To answer your question setting the rootViewController is not the correct way. Present the new vc modally through the presentViewController method.
A better way is to build the navigation vc and present it over your main vc (not replace your main vc).
- (void)doSettings
{
NSLog(#"%#", self.navigationController); // prints nothing
SettingsViewController *settingsViewController = [SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController: settingsViewController];
[self presentViewController: navigationController animated:YES completion:^{}];
}
Your main vc might realize (maybe as a delegate) that the settings flow is complete. It can then dismiss the presented navigation controller with:
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
Alternatively, the setting flow could dismiss itself...
// somewhere in the settings vc or a vc it pushes, when we decide settings are done
self.navigationController dismissViewControllerAnimated:YES completion:^{}];
Have navigation in main view and have below line (which will hide navigation bar)
[[self navigationController] setNavigationBarHidden:YES animated:YES];
(I would say have this in viewWillAppear and viewDidLoad both, BUT in viewWillAppear is MUST).
Now in second view, to show navigation bar
[[self navigationController] setNavigationBarHidden:NO animated:YES];
Hope this will solve your problem...

How to add a Back button to a NavigationBar?

How do I add the back button to a navigation bar? I know there are some related questions but none of them helped. I'm not using storyboard nor did I start with the Master Detail template. Is there a setting to do this? or do you add it by code? if so what code do I need? Any help is appreciated! THANKS!
The back button will appear by default if you use a navigationController correctly. The ViewControllers should be this
NavigationController > FirstViewController > SecondViewController
You will need to create navigationController and instantiate with firstVC as the root. From firstVC you can push secondVC and the back button will be there.
The following placed into appDelegate application didFinishLaunchingWithOptions: will load firstVC initially. Place this after you initialize the window...
UIViewController *firstVC = [[UIViewController alloc] initWithNibName:#"firstVC" bundle:nil];
// any setupup of firstVC
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:firstVC];
[self.window setRootViewController:navCon];
Then in your firstVC class if you want to push secondVC
in firstVC.m:
UIViewController *secondVC = [[UIViewController alloc] init];
[self.navigationController pushViewController:secondVC animated:YES];
You don't need to explicitly add a back button. The button will be added automatically when you push controllers into a UINavigationController. That is, a call as:
[navigator pushViewController: controller animated: ...];
creates a back button.

UINavigationController with UITabViewController

I'm building an app which's delegate has a UINavigationController (navigationController). The first view is a UITabViewController (tabView) which has a UINavigationController with a UIViewController with a UITableView which shows some contacts.
What I want to do is to push a new viewcontroller with the contact's info when tapping over a contact in the tableview (over the toppest navController)
I do the following in the appDelegate:
[self.window makeKeyAndVisible];
[self.window addSubview:[navigationController view]];
TabsView *tabsView = [[TabsView alloc] initWithNibName:nil bundle:nil];
[navigationController.view addSubview:[tabsView view]];
tabsView's first tab loads ContactsView.m which has a UINavigationController with all contacts and when someone clicks on one row, it is supposed to push the new view as this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[table deselectRowAtIndexPath:indexPath animated:YES];
ContactSecondView * varContactSecondView = [[ContactSecondView alloc] initWithNibName:nil bundle:nil];
varContactSecondView.title = [[contactsArray objectAtIndex:indexPath.row] name];
[self.navigationController pushViewController:varContactSecondView animated:YES];
[varContactMapView release];
}
But nothing happens when touching in a row.
So I have different files: Delegate with UINavigationController <- UITabViewController <- UIViewController with UINavigationController with UITableView; and I want to push a ViewController into the first navigationcontroller.
How is supposed to access to delegate's navigationController? Am I doing it right?
Edit: If this gives any clue, when I do self.navigationController.title = #"Contacts"; in ContactsView.m, it's not changing the title of the topbar.
Thanks!
Two things.
It is not recommended to embed a UITabBarController in a navigation controller. It is ok to embed a UINavigationController within a UITabBarController. I know it would be a "nice to have" to embed your UITabBarController in the UINavigationController, but you may want to rethink your design so that you follow the iOS design philosophy.
Instead of adding the subview to the window in your appDelegate, try adding which ever controller you are using to the window's rootViewController property i.e,
self.window.rootViewController=navigationController;
I may be wrong, but I think you don't use the correct way to set your view controller in your navigation controller.
You do :
[navigationController.view addSubview:[tabsView view]];
I would use :
[navigationController setViewControllers:[NSArray arrayWithObject:tabsView] animated:NO];

What exactly does presentModalViewController do?

If I have a navigationController which is init with a root view controller MyViewController's instance.
And in that MyViewController's code
I can use
AnotherViewController *vc = [[AnotherViewController alloc] init];
[self presentModalViewController:vc animated:YES];
or
AnotherViewController *vc = [[AnotherViewController alloc] init];
[self.navigationController presentModalViewController:vc animated:YES];
I found these two works the same. Both present the modal view correctly.And I have found that the presented AnotherViewController's "parentViewController" property are all set to the navigation controller.
Why would this happen?the presentModalViewController automatically detect that the self is the subview of the navigation controller and re send the message to navigation controller?
Because MyViewController is the root view controller of the UINavigationController, it gets the convenience method of presentModalViewController: animated: by default. So when you say self.navigationController, it is referring to the same navigationController that presentModalViewController gives you. I think Apple is just trying to make it more intuitive to use the convenience method.

Resources