I'm trying to Use UINavigationController inside a UITabbarController.
this is my Controllers Structure
-UITabbarController(InitialView)
-tabItemOne-DashboardController
-SomeButtons with StoryboardSegue-To-DifferentViewController
-tabItemTwo-OtherController
-tabItemThree-OtherController
Now I want to show the back button when some StoryboardSegue is performed in DashboardViewController.
Let's say I open the app and can see 4 tabbarItems on UITabbarController, in the first tabbar item I have DashboardViewController, in this DashVC I've 3 4 different buttons to show other viewcontrollers. So far so good, everything is working. but once the child viewcontroller from DashVC is on screen, I want to show a back button on the top as UINavigationController do.
I've tried to put the UINavigationController before UITabbarController but its not showing. i've tried to do embed it in DashboardVC but again its not showing.
Do i need to embed seperate UINavigationController with each of childViewControllers of DashboardVC?
Any help is appreciated.
So what you need is this
-UITabbarController(InitialView)
-NavigationController -tabItemOne-DashboardController
-SomeButtons with StoryboardSegue-To-DifferentViewController
-tabItemTwo-OtherController
-tabItemThree-OtherController
and in DashBoardController you need to add this code in
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = true
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.isHidden = false
}
this code is to hide the navigation bar in Dashboard and restoring when pushing another view controller so you back button is not hide.
also if you need the same functionality in the others view controller you should embebed in navigation controllers.
is not exactly your case but all navigation Controller are Childs of the UITabBarController that is the initial viewController.
Consider a storyboard where we have UITabBarController, in it any UIViewController(lets call it VC) embedded in a UINavigationController. We want VC to have a BarButtonItems on its navigation bar. This storyboard is presented by push segue from another storyboard (having another navigation controller).
Everything looks OK in XCode, but navigation bar does not change in VC at the runtime. However when I change presenting this storyboard from push to modal, everything seems to be fine. IMHO it is because of embedding the navigation controller but I do not see any reason why it is not working. Any idea how to fix it legally (presenting by push) and without any pain would be helpful.
Thanks in advance
So I think you will have to employ some code to fix your issue but not much. I built a test project to test this and will attach images along with code.
First if I understand you correctly you have a navigationController push the new storyboard in question. See attached image.
I named the storyboard being pushed because that is what is happening. Then in my storyboard named Push here is the setup.
In the first view controller of the tabbarcontroller I added the below code. Obviously this hides the navigation controller that pushed us here. If you then visit controller number 2 our new navigation controller and items show. If hiding the navigation controller in the tabbarcontroller view controller 1 is not what you want to do then. continue reading.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//or to unhide from returning the opposite ->self.parent?.navigationController?.isNavigationBarHidden = true
self.parent?.navigationController?.isNavigationBarHidden = true
}
If you did not want to hide the navigation controller in the first view controller but when visiting controller 2 you want to see your items then add this to your viewWillAppear and in the first controller in viewWillAppear change the code from true to false.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Do any additional setup after loading the view, typically from a nib.
self.parent?.navigationController?.isNavigationBarHidden = true
}
This hides the parent navigation controller as basically that was covering up your navigation controller in your example. So above hides the parent navigation controller. This is also why presenting modally worked. Your navigation controller was hidden from the start. Hope this helps.
**Edit
If you want the navigation controller in tab 2 view controller but you want to keep the parent in tab one to be able to go back with the back button you can set this in viewWillAppear instead so it would look like this in view controller 1.
//tabcontroller vc 1
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
And in tabcontroller view controller 2 with the item in the bar you could do this.
//tabbarcontroller vc 2 with own navigationcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.parent?.navigationController?.isNavigationBarHidden = true
}
Finally if you want the back button visible in both controllers but want different right buttons do it programmatically in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(FirstViewController.editSomthing)), animated: true)
}
And if you want to remove it in the other controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.rightBarButtonItem = nil;
}
In Both of the above examples directly above this, we are keeping the parent navigation controller so you would not need to embed your view controllers of the tab controller inside uinavigation controller.
You could also use a combo of the above code if you want the hide/show parent navigation controller in viewWillAppear as well. Some of this is dependent on the view hierarchy you choose now and in the future.
I am having an application in which 4 view controllers are added to the tabbar.
The first view controller is landing page.
The view controller is added in to the tab bar by following way.
SNZLandingViewController *landingViewController = [[UIStoryboard landingStoryboard] instantiateViewControllerWithIdentifier:SNZLandingIdentifier];
UINavigationController *landingNavigationController = [[UINavigationController alloc] initWithRootViewController:landingViewController];
[tabBarController addChildViewController:landingNavigationController];
Then from landing page when the user taps a button i am pushing a view controller
ProductsDetailsViewController *detailsPage = [[UIStoryboard storyboardWithName:#"ProductsDetailsViewController" bundle:nil] instantiateViewControllerWithIdentifier:#"ProductsDetailsViewIdentifier"];
[self.navigationController pushViewController:detailsPage animated:YES];
Now the details page appears. But on tapping "back" in details page, the landing page appears and is completely black.
One weird scenario that is happening is that, the landing page is black while navigation animation is taking place. Am i missing some thing here.
any clue or suggestion is welcome.
So you don't want the navigation bar at the landing page at all?
Put this to your landing Page:
override func viewWillAppear(animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
and this to your followup viewcontroller:
override func viewWillAppear(animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillAppear(animated)
}
taken from here: iPhone hide Navigation Bar only on first page
Please, help me to hide navigation controller in root view. I've found the solution to write [navigationController setNavigationBarHidden:YES] in every view controller which I need. Well, it works but only for the first time: I run application, in root view I don't have navigation, then I go to the second view – the navigation appears, OK. But then I press "Back" in navigation controller, and navigation from root view hasn't disappear. I work with xib.
In rootViewController
-(void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
In second View(next to rootViewController)
-(void)viewDidLoad:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:NO animated:NO];
}
Try this out
Use this line of code on all your view controllers
[navigationController setNavigationBarHidden: YES animated:YES];
If no animation is needed you can do animated:NO and add this code in viewDidLoad or put it viewWillAppear or viewDidAppear.
In your case
[navigationController setNavigationBarHidden:YES animated:NO];
on your rootViewController,
and
[navigationController setNavigationBarHidden:NO animated:YES];
on your other viewControllers
Hide Navigation bar all view controller's view will appear method.
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[navigationCOntroller setNavigationBarHidden:YES animated:animated];
}
I believe you are using tab bar controller.
Your code is working for first view controller as you have code there only. For second view controller, it goes default and hence for second view controller you see navigation bar.
To hide navigation bar for all view controller, put hidden code in all view controllers viewWillAppear
-(void)viewWillAppear:(BOOL)animated
{
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
For Swift developers
Swift 4.2
RootViewController.swift
class RootViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
}
NextViewController.swift
class NextViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.setNavigationBarHidden(false, animated: false)
}
}
I have the code below that hides and shows the navigational bar. It is hidden when the first view loads and then hidden when the "children" get called. Trouble is that I cannot find the event/action to trigger it to hide again when they get back to the root view....
I have a "test" button on the root page that manually does the action but it is not pretty and I want it to be automatic.
-(void)hideBar
{
self.navController.navigationBarHidden = YES;
}
-(void)showBar
{
self.navController.navigationBarHidden = NO;
}
The nicest solution I have found is to do the following in the first view controller.
Objective-C
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:animated];
[super viewWillDisappear:animated];
}
Swift
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillDisappear(animated)
}
This will cause the navigation bar to animate in from the left (together with the next view) when you push the next UIViewController on the stack, and animate away to the left (together with the old view), when you press the back button on the UINavigationBar.
Please note also that these are not delegate methods, you are overriding UIViewController's implementation of these methods, and according to the documentation you must call the super's implementation somewhere in your implementation.
Another approach I found is to set a delegate for the NavigationController:
navigationController.delegate = self;
and use setNavigationBarHidden in navigationController:willShowViewController:animated:
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated
{
// Hide the nav bar if going home.
BOOL hide = viewController != homeViewController;
[navigationController setNavigationBarHidden:hide animated:animated];
}
Easy way to customize the behavior for each ViewController all in one place.
One slight tweak I had to make on the other answers is to only unhide the bar in viewWillDisappear if the reason it is disappearing is due to a navigation item being pushed on it. This is because the view can disappear for other reasons.
So I only unhide the bar if this view is no longer the topmost view:
- (void) viewWillDisappear:(BOOL)animated
{
if (self.navigationController.topViewController != self)
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
[super viewWillDisappear:animated];
}
I would put the code in the viewWillAppear delegate on each view being shown:
Like this where you need to hide it:
- (void)viewWillAppear:(BOOL)animated
{
[yourObject hideBar];
}
Like this where you need to show it:
- (void)viewWillAppear:(BOOL)animated
{
[yourObject showBar];
}
The currently accepted answer does not match the intended behavior described in the question. The question asks for the navigation bar to be hidden on the root view controller, but visible everywhere else, but the accepted answer hides the navigation bar on a particular view controller. What happens when another instance of the first view controller is pushed onto the stack? It will hide the navigation bar even though we are not looking at the root view controller.
Instead, #Chad M.'s strategy of using the UINavigationControllerDelegate is a good one, and here is a more complete solution. Steps:
Subclass UINavigationController
Implement the -navigationController:willShowViewController:animated method to show or hide the navigation bar based on whether it is showing the root view controller
Override the initialization methods to set the UINavigationController subclass as its own delegate
Complete code for this solution can be found in this Gist. Here's the navigationController:willShowViewController:animated implementation:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
/* Hide navigation bar if root controller */
if ([viewController isEqual:[self.viewControllers firstObject]]) {
[self setNavigationBarHidden:YES animated:animated];
} else {
[self setNavigationBarHidden:NO animated:animated];
}
}
in Swift 3:
override func viewWillAppear(_ animated: Bool) {
navigationController?.navigationBar.isHidden = true
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
if (navigationController?.topViewController != self) {
navigationController?.navigationBar.isHidden = false
}
super.viewWillDisappear(animated)
}
Give my credit to #chad-m 's answer.
Here is the Swift version:
Create a new file MyNavigationController.swift
import UIKit
class MyNavigationController: UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if viewController == self.viewControllers.first {
self.setNavigationBarHidden(true, animated: animated)
} else {
self.setNavigationBarHidden(false, animated: animated)
}
}
}
Set your UINavigationController's class in StoryBoard to MyNavigationController
That's it!
Difference between chad-m's answer and mine:
Inherit from UINavigationController, so you won't pollute your rootViewController.
use self.viewControllers.first rather than homeViewController, so you won't do this 100 times for your 100 UINavigationControllers in 1 StoryBoard.
After multiple trials here is how I got it working for what I wanted.
This is what I was trying.
- I have a view with a image. and I wanted to have the image go full screen.
- I have a navigation controller with a tabBar too. So i need to hide that too.
- Also, my main requirement was not just hiding, but having a fading effect too while showing and hiding.
This is how I got it working.
Step 1 - I have a image and user taps on that image once. I capture that gesture and push it into the new imageViewController, its in the imageViewController, I want to have full screen image.
- (void)handleSingleTap:(UIGestureRecognizer *)gestureRecognizer {
NSLog(#"Single tap");
ImageViewController *imageViewController =
[[ImageViewController alloc] initWithNibName:#"ImageViewController" bundle:nil];
godImageViewController.imgName = // pass the image.
godImageViewController.hidesBottomBarWhenPushed=YES;// This is important to note.
[self.navigationController pushViewController:godImageViewController animated:YES];
// If I remove the line below, then I get this error. [CALayer retain]: message sent to deallocated instance .
// [godImageViewController release];
}
Step 2 - All these steps below are in the ImageViewController
Step 2.1 - In ViewDidLoad, show the navBar
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSLog(#"viewDidLoad");
[[self navigationController] setNavigationBarHidden:NO animated:YES];
}
Step 2.2 - In viewDidAppear, set up a timer task with delay ( I have it set for 1 sec delay). And after the delay, add fading effect. I am using alpha to use fading.
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"viewDidAppear");
myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(fadeScreen) userInfo:nil repeats:NO];
}
- (void)fadeScreen
{
[UIView beginAnimations:nil context:nil]; // begins animation block
[UIView setAnimationDuration:1.95]; // sets animation duration
self.navigationController.navigationBar.alpha = 0.0; // Fades the alpha channel of this view to "0.0" over the animationDuration of "0.75" seconds
[UIView commitAnimations]; // commits the animation block. This Block is done.
}
step 2.3 - Under viewWillAppear, add singleTap gesture to the image and make the navBar translucent.
- (void) viewWillAppear:(BOOL)animated
{
NSLog(#"viewWillAppear");
NSString *path = [[NSBundle mainBundle] pathForResource:self.imgName ofType:#"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:path];
self.imgView.image = theImage;
// add tap gestures
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[self.imgView addGestureRecognizer:singleTap];
[singleTap release];
// to make the image go full screen
self.navigationController.navigationBar.translucent=YES;
}
- (void)handleTap:(UIGestureRecognizer *)gestureRecognizer
{
NSLog(#"Handle Single tap");
[self finishedFading];
// fade again. You can choose to skip this can add a bool, if you want to fade again when user taps again.
myTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(fadeScreen) userInfo:nil repeats:NO];
}
Step 3 - Finally in viewWillDisappear, make sure to put all the stuff back
- (void)viewWillDisappear: (BOOL)animated
{
self.hidesBottomBarWhenPushed = NO;
self.navigationController.navigationBar.translucent=NO;
if (self.navigationController.topViewController != self)
{
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
[super viewWillDisappear:animated];
}
In case anyone still having trouble with the fast backswipe cancelled bug as #fabb commented in the accepted answer.
I manage to fix this by overriding viewDidLayoutSubviews, in addition to viewWillAppear/viewWillDisappear as shown below:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
//*** This is required to fix navigation bar forever disappear on fast backswipe bug.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.navigationController?.setNavigationBarHidden(false, animated: false)
}
In my case, I notice that it is because the root view controller (where nav is hidden) and the pushed view controller (nav is shown) has different status bar styles (e.g. dark and light). The moment you start the backswipe to pop the view controller, there will be additional status bar colour animation. If you release your finger in order to cancel the interactive pop, while the status bar animation is not finished, the navigation bar is forever gone!
However, this bug doesn't occur if status bar styles of both view controllers are the same.
If what you want is to hide the navigation bar completely in the controller, a much cleaner solution is to, in the root controller, have something like:
#implementation MainViewController
- (void)viewDidLoad {
self.navigationController.navigationBarHidden=YES;
//...extra code on view load
}
When you push a child view in the controller, the Navigation Bar will remain hidden; if you want to display it just in the child, you'll add the code for displaying it(self.navigationController.navigationBarHidden=NO;) in the viewWillAppear callback, and similarly the code for hiding it on viewWillDisappear
The simplest implementation may be to just have each view controller specify whether its navigation bar is hidden or not in its viewWillAppear:animated: method. The same approach works well for hiding/showing the toolbar as well:
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setToolbarHidden:YES/NO animated:animated];
[super viewWillAppear:animated];
}
Hiding navigation bar only on first page can be achieved through storyboard as well. On storyboard, goto Navigation Controller Scene->Navigation Bar. And select 'Hidden' property from the Attributes inspector. This will hide navigation bar starting from first viewcontroller until its made visible for the required viewcontroller.
Navigation bar can be set back to visible in ViewController's ViewWillAppear callback.
-(void)viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:YES animated:animated];
[super viewWillAppear:animated];
}
Swift 4:
In the view controller you want to hide the navigation bar from.
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(true, animated: animated)
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.setNavigationBarHidden(false, animated: animated)
super.viewWillDisappear(animated)
}
By implement this code in your ViewController you can get this effect
Actually the trick is , hide the navigationBar when that Controller is launched
- (void)viewWillAppear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:YES animated:YES];
[super viewWillAppear:animated];
}
and unhide the navigation bar when user leave that page do this is viewWillDisappear
- (void)viewWillDisappear:(BOOL)animated {
[self.navigationController setNavigationBarHidden:NO animated:YES];
[super viewWillDisappear:animated];
}