Showing/hiding navigation bar with smooth animation

46,468

Solution 1

You can use [navigationController setNavigationBarHidden:YES animated:YES] to hide the bar smoothly.

Reference

Solution 2

This nifty bit of code animates the navigation bar hiding with no UI issues:

[navigationController setNavigationBarHidden: YES animated:YES]

But...

  1. Use the self.navigationController.navigationBarHidden property for checks in the code instead of the self.navigationController.navigationBar.hidden property. This will save you a lot of pain from unexpected UI positioning problems.
  2. Take care to place this method in - (void)viewWillAppear:(BOOL)animated or later in the view lifecycle. This is recommended because if you do it in - (void)viewDidLoad for instance, you will get an ugly black rectangular view during animations from a view which displays its navigation bar to a view which doesn't! For example, if your home view has its navigation bar hidden but all its children have the navigation bar shown, when you pop to home view, the animation will show a black bar in place of the navigation bar until the animation completes

Solution 3

You can customize the navigation bar animation and duration by the following methods. It will provide you callback once animation will be completed.

   // pass a param to describe the state change, an animated flag and a completion block matching UIView animations completion
    - (void)setNavigationBarVisible:(BOOL)visible animated:(BOOL)animated completion:(void (^)(BOOL))completion {

        // fail if the current state matches the desired state
        if ([self navigationBarIsVisible] == visible) return completion(YES);

        // get a frame calculation ready
        CGFloat nheight = self.navigationController.navigationBar.frame.size.height;
        CGFloat noffsetY = (visible)? -nheight : nheight;

        // zero duration means no animation
        CGFloat duration = (animated)? 0.3 : 0.0;

        [UIView animateWithDuration:duration animations:^{
            CGRect nframe = self.navigationController.navigationBar.frame;
            self.navigationController.navigationBar.frame = CGRectOffset(nframe, 0, noffsetY);
        } completion:completion];
    }

    // know the current state of the navigation bar
    - (BOOL)navigationBarIsVisible {
        return self.navigationController.navigationBar.frame.origin.y < CGRectGetMinY(self.view.frame);
    }

    // Show or Hide navigation bar
    [self setNavigationBarVisible:![self navigationBarIsVisible] animated:YES completion:^(BOOL finished) {
        NSLog(@"navigation bar finished");
    }];

Before hide a Navigation bar:

Before hide a Navigation bar:

After hide a Navigation bar:

After hide a Navigation bar:

Share:
46,468
4thSpace
Author by

4thSpace

Updated on February 15, 2020

Comments

  • 4thSpace
    4thSpace about 4 years

    I have a navigation based app. The first view (rootcontroller) starts with three large buttons only. No navigationbar. From there, everything else is tableviews and have navigation bars. I'm doing this to show/hide the navigation bar:

    MyAppAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    appDelegate.navigationController.navigationBar.hidden = NO;
    

    Once I leave the root controller, the navigation bar will jerk into place and lay on top of the tableview, rather than pushing it down. It clips the top part of the tableview. Going back to the root controller isn't smooth in how the navigation bar disappears. Is there a smoother/better way to do accomplish hiding the navigation bar for the root controller only?

  • illegal-immigrant
    illegal-immigrant over 11 years
    mixing navigationController.navigationBar.hidden = NO with setNavigationBarHidden leads to some weird stuff, hiding just did not work for me. When using single approach works fine.
  • Whoa
    Whoa over 9 years
    When would you recommend doing it in the view lifecycle?
  • codeburn
    codeburn over 9 years
    @Whoa : I'd recommend doing it in the 'viewWillAppear:' method.
  • Stephen Paul
    Stephen Paul over 9 years
    I never place transitioning events to elements as significant as the navigation bar in viewDidLoad... It's unwise to do so, especially if the child and/or parents VCs have interactive dismissal. Place the nav bar animations in viewWillAppear or later in the lifecycle as mentioned above.
  • D. Mika
    D. Mika about 5 years
    I don't think it's a good idea to move the UINavigationController's UINavigationBar. It's the UINavigationController's job to manage the bar and it could lead to UI anomalies if the bar is somewhere else than the controller thinks.
  • Peter Johnson
    Peter Johnson almost 5 years
    in iOS13 beta I'm seeing the ugly animations again when transitioning from a home view with no navigation bar to child VCs that have one, even though the code is in viewWillAppear. The home screen graphics show through in the area behind the large titles. Any suggestions?