14

I want to toggle the visibility of the status bar on tap, just like it does in the Photos app.

Prior to iOS 7, this code worked well:

-(void)setStatusBarIsHidden:(BOOL)statusBarIsHidden {

    _statusBarIsHidden = statusBarIsHidden;

    if (statusBarIsHidden == YES) {

        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];


    }else{

        [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationFade];

    }

}

But I can't get it to work in iOS 7. All the answers that I found only offer suggestions for permanently hiding the bar but not toggling.

Yet, there must be a way since Photos does it.

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
anna
  • 2,723
  • 4
  • 28
  • 37
  • possible duplicate of [Cannot hide status bar in iOS7](http://stackoverflow.com/questions/18059703/cannot-hide-status-bar-in-ios7) – Shawn Oct 01 '13 at 19:54

8 Answers8

44

By default on iOS 7 or above, to hide the status bar for a specific view controller, do the following:

  1. if the view controller you want to hide the status bar with is being presented modally and the modalPresentationStyle is not UIModalPresentationFullScreen, manually set modalPresentationCapturesStatusBarAppearance to YES on the presented controller before it is presented (e.g. in -presentViewController:animated:completion or -prepareForSegue: if you're using storyboards)
  2. override -prefersStatusBarHidden in the presented controller and return an appropriate value
  3. call setNeedsStatusBarAppearanceUpdate on the presented controller

If you want to animate it's appearance or disappearance, do step three within an animation block:

[UIView animateWithDuration:0.33 animations:^{
    [self setNeedsStatusBarAppearanceUpdate];
}];

You can also set the style of animation by returning an appropriate UIStatusBarAnimation value from -preferredStatusBarUpdateAnimation in the presented controller.

followben
  • 9,067
  • 4
  • 40
  • 42
  • +eleventyone for the pointer to preferredStatusBarUpdateAnimation – xaphod Jul 09 '15 at 12:39
  • the animation is not working at all. Where should I put it? viewWillAppear and viewDidAppear none works – Wingzero Oct 29 '15 at 06:09
  • wow, you last sentence `returning an appropriate UIStatusBarAnimation` matters a lot. I add a BOOL to detect if view appreared, and return different style for the BOOL did the trick. But I am confused why apple choose this way. `[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];` did all the trick without pain – Wingzero Oct 29 '15 at 06:28
  • 2
    modalPresentationCapturesStatusBarAppearance saved me. Nice answer. – Jesse Naugher Jun 20 '16 at 18:44
7

First set View controller-based status bar appearance in Info.plist to YES

This Swift Example shows how to toggle the StatusBar with an Animation, after pressing a Button.

import UIKit

class ToggleStatusBarViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func prefersStatusBarHidden() -> Bool {
        return !UIApplication.sharedApplication().statusBarHidden
    }

    override func preferredStatusBarUpdateAnimation() -> UIStatusBarAnimation {
        return UIStatusBarAnimation.Slide
    }

    @IBAction func toggleStatusBar(sender: UIButton) {
        UIView.animateWithDuration(0.5,
            animations: {
                self.setNeedsStatusBarAppearanceUpdate()
        })
    }
}
coco
  • 4,912
  • 2
  • 25
  • 33
5

I was able to simplify @Jon's answer and still get behavior indistinguishable from the Photos app on iOS 7. It looks like the delayed update when showing isn't necessary.

- (IBAction)toggleUI:(id)sender {
    self.hidesUI = !self.hidesUI;

    CGRect barFrame = self.navigationController.navigationBar.frame;

    CGFloat alpha = (self.hidesUI) ? 0.0 : 1.0;
    [UIView animateWithDuration:0.33 animations:^{
        [self setNeedsStatusBarAppearanceUpdate];
        self.navigationController.navigationBar.alpha = alpha;
    }];

    self.navigationController.navigationBar.frame = CGRectZero;
    self.navigationController.navigationBar.frame = barFrame;
}

- (BOOL)prefersStatusBarHidden {
    return self.hidesUI;
}
Justin Anderson
  • 2,100
  • 3
  • 23
  • 22
4

This might be considered a bit of a hack but it's the closest I've come to reproducing the effect. There's still one minor issue. When fading out, you can see the navigation bar being resized from the top. It's subtle enough but still not a perfect fade. If anyone knows how to fix it, let me know!

- (BOOL)prefersStatusBarHidden {

    if (_controlsAreHidden == YES)
        return YES;
    else
        return NO;
}

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation {

    return UIStatusBarAnimationFade;
}

-(void)setControlsAreHidden:(BOOL)controlsAreHidden {

    _controlsAreHidden = controlsAreHidden;

    if (controlsAreHidden == YES) {

        // fade out
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        [UIView animateWithDuration:0.3 animations:^ {


            [self setNeedsStatusBarAppearanceUpdate];

            self.navigationController.navigationBar.alpha = 0;



        }];


        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);



    }else{


        // fade in
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);

        [UIView animateWithDuration:0.3 animations:^ {

            [self setNeedsStatusBarAppearanceUpdate];

            self.navigationController.navigationBar.alpha = 1;

        }];


    }

}
anna
  • 2,723
  • 4
  • 28
  • 37
  • Did you ever figure out how to fix the navigation bar jump? – SAHM Mar 07 '16 at 07:14
  • @SAHM Oof, this is an old question and I don't remember that project anymore. Looks like there's a lot of new solutions on this page, so hopefully some of the newer suggestions work better. – anna Mar 08 '16 at 21:27
3

This code works perfectly fine:

-(void)setControlsAreHidden:(BOOL)controlsAreHidden {

    if (_controlsAreHidden == controlsAreHidden)
        return;

    _controlsAreHidden = controlsAreHidden;


    UINavigationBar * navigationBar = self.navigationController.navigationBar;

    if (controlsAreHidden == YES) {

        // fade out
        //

        CGRect barFrame = self.navigationController.navigationBar.frame;

        [UIView animateWithDuration:0.3 animations:^ {
            [self setNeedsStatusBarAppearanceUpdate];
            self.navigationController.navigationBar.alpha = 0;
        }];

        self.navigationController.navigationBar.frame = CGRectZero;
        self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 44);

    } else {

        // fade in
        //
        [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
            [self setNeedsStatusBarAppearanceUpdate];
        }];

        double delayInSeconds = 0.01;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

            [self.navigationController setNavigationBarHidden:NO animated:NO];
            navigationBar.alpha = 0;
            [UIView animateWithDuration:UINavigationControllerHideShowBarDuration animations:^ {
                navigationBar.alpha = 1;
            }];

        });

    }

}
Jon
  • 31
  • 3
3

Actually there is now need to mess with navigation bar frames. You can achieve smooth animation just by using 2 separate animation blocks. Something like this should work just fine.

@property (nonatomic, assign) BOOL controlsShouldBeHidden;

...

- (void)setControlsHidden:(BOOL)hidden animated:(BOOL)animated {

    if (self.controlsShouldBeHidden == hidden) {
        return;
    }

    self.controlsShouldBeHidden = hidden;

    NSTimeInterval duration = animated ? 0.3 : 0.0;

    [UIView animateWithDuration:duration animations:^(void) {

        [self setNeedsStatusBarAppearanceUpdate];

    }];

    [UIView animateWithDuration:duration animations:^(void) {

        CGFloat alpha = hidden ? 0 : 1;
        [self.navigationController.navigationBar setAlpha:alpha];

    }];

}

- (BOOL)prefersStatusBarHidden {

    return self.controlsShouldBeHidden;

}

For compatibility with iOS 6 just make sure to check [self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]

Alexander Dvornikov
  • 1,074
  • 8
  • 13
1

The way to resolve this depends on the value of the "View controller-based status bar appearance" setting in your app's plist.

If "View controller-based status bar appearance" is NO in your plist, then this code should work:

[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade];

If "View controller-based status bar appearance" is on, in your view controllers, add this method:

- (BOOL) prefersStatusBarHidden {
    // I've hardcoded to YES here, but you can return a dynamic value to meet your needs for toggling
    return YES;
}

For toggling, when you want to change whether the status bar is hidden/shown based on the value of the above method, your view controller can call the setNeedsStatusBarAppearanceUpdate method.

Greg
  • 33,450
  • 15
  • 93
  • 100
  • I tried this but it doesn't do what I want. The fade animation never happens. When "prefersStatusBarHidden" gets triggered, it hides the status bar but also changes the bounds, so that my navigation bar jumps up. I want the entire block of 'navigation bar + status bar' to simply fade in/fade out. – anna Oct 01 '13 at 20:04
  • Ah, didn't realize you had a navigation bar in play as well. What happens if at the same time you call setNeedsStatusBarAppearanceUpdate, you also start animating the disappearance of the navigation bar by using UINavigationController's setNavigationBarHidden:animated: method? https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instm/UINavigationController/setNavigationBarHidden:animated: – Greg Oct 01 '13 at 20:07
  • Ok, I was able to get them both to fade but it's still changing the bounds, so the navigation bar is sliding up as it's fading. Any thoughts to prevent that? – anna Oct 01 '13 at 20:14
  • Are you fading the status bar out, or sliding it out? If the former, I would have thought the navigation bar would stay put. – Greg Oct 01 '13 at 20:27
  • I'm just fading it but my guess is that "prefersStatusBarHidden" triggers a change in bounds on the view – anna Oct 01 '13 at 20:29
  • Yeah, I'm not sure then; sorry. – Greg Oct 01 '13 at 20:33
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38452/discussion-between-greg-and-anna) – Greg Oct 01 '13 at 20:34
1

To correct this issue with navigation bar sliding up when fading, you should add the following code:

self.navigationController.navigationBar.frame = CGRectZero;

into your "fade in" section before the following code line:

self.navigationController.navigationBar.frame = CGRectMake(0, 20, barFrame.size.width, 64);

This is necessary because the frame is the same and setting the same frame will be ignored and will not stop the navigation bar from sliding. Therefore you need to change the frame to something different and then set it again to the correct frame to trigger the change.

Bond007
  • 51
  • 1
  • 1