ChatGPT解决这个技术问题 Extra ChatGPT

iPhone hide Navigation Bar only on first page

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;
}

G
Geri Borbás

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.


This totally rocks! I had been struggling with this for at least a day. Thanks!!!
WARNING: This creates a very bad bug when doing a fast backswipe. Assume A (no navbar) and B (with navbar) pushed onto the stack. When on view B and doing a fast backswipe, but release early enough to stay on B, the navbar still gets hidden. Now there is no way to go back anymore. This is due to animated=YES. I know it looks ugly with animated=NO, but it seems when the animation for hiding the navbar is not yet finished, then the animation for showing it again is ignored. No solution yet.
In Swift: override func viewWillAppear(animated: Bool) { self.navigationController?.setNavigationBarHidden(true, animated: true) super.viewWillAppear(true) } override func viewWillDisappear(animated: Bool) { self.navigationController?.setNavigationBarHidden(false, animated: false) super.viewWillDisappear(true) }
Question was answered in 2010 and helps me at the end of 2015! Thank you.
Now that's what I call a legendary answer. Superb trick mate. Even working after decades...Implemented the same in swift, working flawlessly. +1 for your answer @Alan Rogers
B
Borzh

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.


When will this get called?
Perfect solution. This should be the accepted answer. Thanks!
Perfect answer. It also works in case we cannot override viewWillAppear and viewWillDisappear methods on the first view controller.
Awesome. The chosen answer works ok, however only in simple apps. This answer works when nav bar is in a tab controller and pushes / presents various VCs in various ways.
This is the best answer. The top answer may occur a bug as @fabb's description.
佚名

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];
}

+1, you usually don't want to show the navigation bar when pushing a modal dialog.
P
Pablo Santa Cruz

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];
}

Lee, if this fixed your problem, mark Pablo's as the "solution" answer.
The only problem with this is that the navigation bar "pops" out and into view as you navigate from one view to the next. Is it possible just to have the navigation bar not there on the first view, and when the second view slides into place, it has the nav bar, without any popping?
@henning To make the NavBar slide in/out as you would expect, you need to use setNavigationBarHidden:animated:. See Alan Rogers' answer below (which should really be marked as the "solution").
This answer is slightly wrong (viewWill/DidAppear) should be calling super. Also See my answer below for a solution where you don't need to add it to EVERY view controller.
C
Community

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];
    }
}

This is a more appropriate answer than the accepted one
E
Eugene Braginets

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)
}

could you explain why you check for != self?
@Kitson, check the user486646' answer: 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
It seems that if you use the navcontroller.navagationBarHidden it will break the entire navigation controller (no swiping back and forth). To get it to work I used navigationController?.navigationBar.hidden instead. Swiping still works and it doesn't leave empty space because it seems to be inside a stackview or something
A
AI Lion

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.


Think this is the cleanest answer. Thanks
C
CristiC

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];
}

a
aunnnn

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.


P
Paras Joshi

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


P
Paras Joshi

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];
}

Actually, my suggestion only makes sense for the toolbar, since hiding the navigation bar without a matching call to show it would leave users unable to navigate back from the current view.
B
BDL

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];                                                  
}

J
John Riselvato

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)
}

D
Dhiru

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];
}