I have a tabbar application with a navigation bar. For one of the actions I am instantiating a navigation controller programatically and adding a view with tableview. I want to remove this navigation bar and tableview programatically clicking a button on the new navigation bar. how to do this ?
I tried popview but it is not poping out.
To hide it
[[self navigationController] setNavigationBarHidden:YES];
If you have added a UINavigationController on top of another UIViewController, then from the uiNaviagation controller, you will not be able to remove the navigation bar even if you remove the current view, and all the subview. (the parent uiviewcontroller also get the nav bar)
One way to fix this is, access the app delegate, and remove the top view from the window before
adding the UINavigationController
AppDelegate *dg = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *ar = [[dg window] subviews];
//then remove all the views in ar
//then add uinavcontroller
[[dg window] addSubView:[uinavcontroller view]];
then add UINavigationController, when you want to replace the UInavigationcontroller with the first UiViewController. do the similar steps like above.
Related
I'm trying to select one of my UINavigationControllers in the UITabBar.viewControllers array.
I previously tried it with setting the UITabbarController.selectedIndex, but the Apple documentation says:
"To select the More navigation controller itself, you must change the value of the selectedViewController property instead."
AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
UINavigationController *navController = [appDelegate.objTabBarController.viewControllers objectAtIndex:5];
[appDelegate.objTabBarController setSelectedViewController:navController];
Doing so is fine but when I change back to the MoreViewController's list view, the icon on the left side is gone and won't come back.
Everything ok, when selecting it with the finger.
Bug when selecting programmatically -> image is gone
Any suggestions what I'm doing wrong?
Best regards,
Steve
Selecting the More Navigation Controller
To select the moreNavigationController, you must set the selectedViewController:
self.tabBarController.selectedViewController = self.tabBarController.moreNavigationController;
The reason for that is simple: The moreNavigationController is not inside the viewControllers of the tab bar controller, so it can't be selected by index.
To select one of the "more" controllers directly, you can either set the selectedViewController property:
self.tabBarController.selectedViewController = viewController5;
Or you can set the selectedIndex:
self.tabBarController.selectedIndex = 5;
Fixing the Disappearing Tab Bar Image
The disappearing tab bar image is caused by using a navigation controller (for the settings) inside a navigation controller (the moreNavigationController generated by the tab bar controller). To fix it, there are two solutions:
Don't add navigation controllers for the more section, but add the controllers directly. The controller structure would look like this, assuming the controllers in all tabs need navigation:
tabBarController
+ navigationController
+ viewController0
+ navigationController
+ viewController1
+ navigationController
+ viewController2
+ navigationController
+ viewController3
+ viewController4
+ viewController5
Set the tabBarItem of the settings controller on its navigation controller (you only need to do this once):
UINavigationController *settingsNavigationController = [appDelegate.objTabBarController objectAtIndex:5];
UIViewController *settingsRootController = settingsNavigationController.viewControllers[0];
settingsNavigationController.tabBarItem = settingsRootController.tabBarItem;
I ran into this same problem. It seems that the navigation controller was somehow losing track of the view controller. In my case, the navigation controller whose image was disappearing was at index 7 in the tabBarController. The navigation controller was supposed to contain a viewController of class SettingsViewController. But sometimes it lost it.
To fix it, I had to add code to two classes -- the app delegate and SettingsViewController. In the app delegate:
-(void) fixSettingsNavigationController {
UITabBarController* tab = self.tabBarController;
NSArray* vcs = tab.viewControllers;
NSInteger nVCs = vcs.count;
if (nVCs > 7) {
UIViewController* settingsContainer = vcs[7];
UINavigationController* settingsContainerNav = (UINavigationController*)settingsContainer;
if ([settingsContainerNav isKindOfClass: [UINavigationController class]]) {
NSArray* settingsNavVCs = settingsContainerNav.viewControllers;
NSInteger count = settingsNavVCs.count;
SettingsViewController* svc = self.settingsViewController;
if (!count) {
// The container navigation controller has lost track of the settings view controller.
settingsContainerNav.viewControllers = [NSArray arrayWithObject:svc];
}
}
}
}
And in SettingsViewController:
-(void) viewWillDisappear:(BOOL)animated {
MyAppDelegate* appDel = [[UIApplication sharedApplication] delegate];
[appDel fixSettingsNavigationController];
[super viewWillDisappear: animated];
}
I ran to the very similar situation, where I want to select a View controller inside MoreViewController programmatically.
As you said in your question, apple don't allow selecting a view controller that is inside MoreViewController (Or not visible in tabbar).
In my case, I don't need default "More" features of reordering tabs in tabbar.
So, I did following things
Created a UITableViewControlelr as my 5th tab, set its Title to more and custom image (Similar to default more image).
In my UITableViewController I listed items that corresponds to ViewControllers. Tapping on which show respective view controller.
When i need to show any view controller that is inside my UITableViewController programatically, i simply set a flag and push my UITableViewController, and in viewDidAppear method of my UITableViewController push desired view controller.
This will also show back button on programatically opened view to list of MoreViewController
I'm trying to add some navigation controller in my app, it's sth likes:
in my index page view controller, I try to initialize the navigation controller like this:
-(void)viewDidLoad{
...
//allocate a navigation controller.
myNavigationController = [[UINavigationController alloc]init];
myNavigationController.delegate = self;
myNavigationController.navigationBar.hidden = YES;
[self.view addSubview:myNavigationController.view];
[myNavigationController pushViewController:tabViewController animated:YES];
[self presentModalViewController:myNavigationController animated:YES];
}
Here, index page view controller is the root view controller of my app, it's just a common UIViewController here.
[myNavigationController pushViewController:tabViewController animated:YES];
The tabViewController here I've pushed into the navigation controller is a custom tabview controller which makes use of a container view to hold the tab button and also holds an navigation controller for tab switching.
The problem here is:
myNavigationController.navigationBar.hidden = YES;
since I've make the navigation bar invisible, it doesn't show when my custom view controller shows, but when I'd like to switch to some other view controller with the navigation controller and I also want the navigation bar visible:
myNavigationController.navigationBar.hidden = NO;
MyViewController *toSwitchNC = [[MyViewController alloc]init];
[myNavigationController pushViewController:toSwitchNC animated:YES];
The navigation bar would never show any more. I've also tried to put:
self.navigationController.navigationBar.hidden = NO
in MyViewController's viewDidLoad, ViewDidAppear or even in the navigation controller's delegate method, it didn't show the navigation bar neither.
So what's wrong with it? Why I initialized the navigation bar to be invisible at first, it will never show again even I set the hidden flag to be false?
Okay, I've got this fixed by removing the navigation controller container in my index page view controller. This might be a stupid question, since apple've formally stated in the developer document that the navigation view controller should be place as root as possible in the view stack. Since IOS is a closed system, who knows WTH is going on under-beneath except Apple.
I'm trying to present a TabBarController on top of another TabBarController. My application's MainWindow.xib looks like this:
Files Owner
First Responder
My App App Delegate
Window
TabBarContoller
+TabBar
+Nav Controller Subclass (a custom class)
+Navigation Bar
+Table View Contoller Subclass (custom class)
+Tab Bar Item
+Second View Controller (not yet hooked up)
I'm trying to display a xib file when an item is clicked in the TableView. This xib file has a TabBarController as it's main view, but when the view displays, the tab bar and navigation bar are both invisible. The code I'm using to display it is:
MyAppAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.customNavController presentModalViewController:customDetailEditViewController animated:YES];
If I use the code below to push the view controller onto the stack, I see the correct navigation bar, but the TabBar from the root view controller is shown instead of the one from the view which has been pushed.
[delegate.customNavController pushViewController:customDetailEditViewController animated:YES];
I even tried removing the TabBarController and manually implementing my own TabBar delegates but the same effect occurs (either no NavigationBar or TabBar, or the NavigationBar/TabBar from the root ViewController).
EDIT: I've uploaded the source to http://mi6.nu/tabcontroller.zip . I'd really appreciate it if someone with a bt more experience with iOS could take a look.
EDIT2: The closest I've come so far is presenting a modal view controller inside the first tabbar, so my view looks like this:
NavigationBar
[ ]
[ ]
[---View---]
[ ]
[ ]
TabBar from the pushed view
TabBar from the root view
To achieve this, I'm using:
UITabBarController *tabBarController = [[UITabBarController alloc] init];
UIViewController *directionsView = [[UIViewController alloc] init];
txtDirections = [[UITextView alloc]initWithFrame:CGRectMake(0,0,self.view.frame.size.width, self.view.frame.size.height)];
[directionsView.view addSubview:txtDirections];
IconPickerViewController *iconPicker = [[IconPickerViewController alloc]init];
tabBarController.viewControllers = [NSArray arrayWithObjects:recipeDetailEditViewController,directionsView,iconPicker, nil];
[directionsView release];
[iconPicker release];
MyAppAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.rootNavController presentModalViewController:recipeDetailEditViewController animated:YES];
This just complicates everything though, since a) It's not ideal as I have two tabbars and b) all controls (all of the editing controls) need to be in the TableViewController so their values can be loaded/saved to edit items. It would be much easier if the pushed view could handle loading/saving and appear on top of the root tabs.
Surely this must be possible?
This was an absolute nightmare, but I eventually got it working by presenting the view modally from the TableViewController when the add button is clicked:
[delegate.rootController presentModalViewController:recipeDetailEditViewController animated:YES];
delegate.rootController is the root TabBarController
The XIB file I'm presenting does not have a TabBarController, but rather a UIView, a Navigation bar and a TabBar. This appears to work when presented modally from inside a NavigationController.
Why are you putting everything in your main.xib?
Try moving the second tab bar out of the main window xib and instead put it inside the customDetailEditViewController.
I am showing a modal view which is a UITableViewController class. For some reason it won't show the navigation bar when I show it. Here is my code:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
detailViewController.navigationController.navigationBarHidden = NO;
[self.navigationController presentModalViewController:detailViewController animated:YES];
detailViewController = nil;
[detailViewController release];
I thought it was shown by default? If it helps, I am calling this from another class that is also a UITableViewController managed by a UINavigationController. Ideas?
When you present a modal view controller it does not use any existing navigation controllers or navigation bars. If all you want is to display a navigation bar, you need to add the navigation bar as a subview of your modal view and present it as you're doing.
If you want to present a modal view controller with navigation functionality, you need to present a modal navigation controller containing your detail view controller instead, like so:
SettingsCreateAccount *detailViewController = [[SettingsCreateAccount alloc] initWithStyle:UITableViewStyleGrouped];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[detailViewController release];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
[navController release];
Your modal controller will manage its own navigation stack.
Here is one way to display navigation bar for those who are using storyboards, suggested by Apple's Tutorial on Storyboard.
Because a modal view controller doesn’t get added to the navigation stack, it doesn’t get a navigation bar from the table view controller’s navigation controller. To give the view controller a navigation bar when presented modally, embed it in its own navigation controller.
In the outline view, select View Controller.
With the view controller selected, choose Editor > Embed In > Navigation Controller.
On iOS 7 and you just want a navigation bar on your modal view controller to show a title and some buttons? Try this magic in your UITableViewController:
// in the .h
#property (strong) UINavigationBar* navigationBar;
//in the .m
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = #"Awesome";
self.navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectZero];
[self.view addSubview:_navigationBar];
[self.navigationBar pushNavigationItem:self.navigationItem animated:NO];
}
-(void)layoutNavigationBar{
self.navigationBar.frame = CGRectMake(0, self.tableView.contentOffset.y, self.tableView.frame.size.width, self.topLayoutGuide.length + 44);
self.tableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.size.height, 0, 0, 0);
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//no need to call super
[self layoutNavigationBar];
}
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self layoutNavigationBar];
}
I want to share how the accepted solution can be used in projects with storyboards:
The simple approach is to put in a storyboard blank navigation controller before the VC which is to be presented modally, so the relations look like:
(Presenter VC) -> presents modally -> (navigation controller having a controller to be presented as its root).
We've tried this approach for a while and noticed that our storyboards become "polluted" by a large number of such intermediate navigation controllers when each! of them is used exclusively for one! presentation of some other controller, that we want to be presented modally with navigation bar.
Our current solution is to encapsulate the code from accepted answer to a custom segue:
#import "ModalPresentationWithNavigationBarSegue.h"
#implementation ModalPresentationWithNavigationBarSegue
- (void)perform {
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:self.destinationViewController];
[self.sourceViewController presentViewController:navigationController animated:YES completion:nil];
}
#end
Having this segue in our project we do not create intermediate navigation controllers in our storyboards anymore, we just use this ModalPresentationWithNavigationBarSegue like:
Presenter VC --> Presentee VC
I hope that this answer will be helpful to people who like to avoid unnecessary duplication in their apps storyboards.
I just wanted to add something to what #Scott said. His answer is definitely the easiest and most accepted way of doing it now with Storyboards, iOS 7 and 8... (and soon, 9).
Definitely adding a view controller to the Storyboard and Embedding it as described by #Scott is the right way to go.
Then, just add the segue by control-dragging from the source view controller to the target (the one you want to show modally), select "Present Modally" when the little view appears with the choices for the type of segue. Probably good to give it a name too (in the example below I use "presentMyModalViewController").
One thing that I needed that was missing is #Scott's case is when you want to actually pass on some data to that modally-presented view controller that is embedded in the navigation controller.
If you grab the segue.destinationViewController, it will be a UINavigationController, not the controller you embedded in the UINavigationController.
So, to get at the embedded view controller inside the navigation controller, here's what I did:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"presentMyModalViewController"]) {
// This could be collapsed, but it's a little easier to see
// what's going on written out this way.
// First get the destination view controller, which will be a UINavigationController
UINavigationController *nvc = (UINavigationController *)segue.destinationViewController;
// To get the view controller we're interested in, grab the navigation controller's "topViewController" property
MyModalViewController *vc = (EmailReceiptViewController *)[nvc topViewController];
// Now that we have the reference to our view controller, we can set its properties here:
vc.myAwesomeProperty = #"awesome!";
}
}
Hope this helps!
If you only need a NavigationBar, you can add an instance of UINavigationBar and assign BarItems to it.
Update: Another solution to this problem would be if the Navigation bar at the root level of the navigation controller could be transparent or not shown. Is there a way to make the Navigation bar at the root level of the navigation controller transparent or not shown?
I have a NIB with a toolbar at the top of my top level UIView and below the toolbar is a tableView. When I use pushViewController on a navigationController to push another UIViewController onto the navigationController, the toolbar is overwritten by the navigation bar. When I pop the current view back to the root view, the toolbar cannot be seen as there is a blank bar across the top. There is now also a gap between the toolbar and the top of the tableView about the size of the toolbar. So the view looks like from the top:
1) shaded blank bar,
2) blank space about the size of a toolbar,
3) tableview
How can I make the toolbar of the top level NIB appear at the top of the UIView after using popViewController?
In the top level view I instantiate a UINavigationController:
self.navigationController = [[UINavigationController alloc] initWithRootViewController:ListsController];
and then in didSelectRowAtIndexPath I push a view controller
ItemsController * Items = [[ItemsController alloc]
initWithNibName:#"Items" bundle:nil] ;
[self.navigationController pushViewController:Items animated:YES];
To get the initial pushed view to display I do the following:
UIView *navView = self.navigationController.view;
CGRect navFrame = navView.frame;
// navFrame.origin.y -= 20;
navView.frame = navFrame;
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
UIWindow *appWindow = appDelegate.window;
[appWindow addSubview:navView];
Any idea how I can get the top level toolbar not to be overwritten when popping back to the top level?
The solution is to make the navigation bar hidden at the root level using:
- (void)viewWillAppear:(BOOL)animated {
[[self navigationController] setNavigationBarHidden:YES animated:YES];
[super viewWillAppear:animated];
[self.table reloadData];
}
Then at the next lower level of the navigation stack, make the navigation bar unhidden:
- (void)viewWillAppear:(BOOL)animated {
[[self navigationController] setNavigationBarHidden:NO animated:YES];
[super viewWillAppear:animated];
[self.table reloadData];
}