Proper way to hide status bar on iOS, with animation and resizing root view

29,265

Solution 1

This works fine and has nothing hard coded.

CGRect appFrame = [[UIScreen mainScreen] applicationFrame];

[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
[UIView animateWithDuration:0.25 animations:^{
    self.navigationController.navigationBar.frame = self.navigationController.navigationBar.bounds;
    self.view.window.frame = CGRectMake(0, 0, appFrame.size.width, appFrame.size.height);
}];

Solution 2

For those that are trying to implement this with view controller-based status bar appearance, you need to implement the prefersStatusBarHidden method in your view controller

 - (BOOL)prefersStatusBarHidden
{
    // If self.statusBarHidden is TRUE, return YES. If FALSE, return NO.
    return (self.statusBarHidden) ? YES : NO;
}

And then, in your button click method:

- (void) buttonClick:(id)sender
{
    // Switch BOOL value
    self.statusBarHidden = (self.statusBarHidden) ? NO : YES;

    // Update the status bar
    [UIView animateWithDuration:0.25 animations:^{
        [self setNeedsStatusBarAppearanceUpdate];
    }];
}

To set the animation style, you use this:

-(UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    return UIStatusBarAnimationSlide;
}

And to customize the style:

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

Solution 3

You can present and then dismiss modal view controller to hide status bar correctly

- (void)toggleStatusBar {
    BOOL isStatusBarHidden = [[UIApplication sharedApplication] isStatusBarHidden];
    [[UIApplication sharedApplication] setStatusBarHidden:!isStatusBarHidden];

    UIViewController *vc = [[UIViewController alloc] init];
    [self presentViewController:vc animated:NO completion:nil];
    [self dismissViewControllerAnimated:NO completion:nil];
    [vc release];
}

I used this code in the "willAnimateRotationToInterfaceOrientation" method for landscape orientation and everything is working correctly. But I don't know if it will work with animation.

Solution 4

Hide or Show status bar that also re-sizes the view:

-(void)statusBar:(BOOL)status {
UIViewController *rootViewController = self.view.window.rootViewController;
UIView *view = rootViewController.view;

// Hide/Unhide the status bar
[[UIApplication sharedApplication] setStatusBarHidden:status]; // BOOL : YES or NO

// statusBar frame
CGRect statusBarFrame = [UIApplication.sharedApplication statusBarFrame];
// Establish baseline frame
CGRect newViewFrame = self.view.window.bounds;

// Check statusBar frame is worth dodging
if (!CGRectEqualToRect(statusBarFrame, CGRectZero)) {
    UIInterfaceOrientation currentOrientation = rootViewController.interfaceOrientation;
    if (UIInterfaceOrientationIsPortrait(currentOrientation)) {
        // If portrait need to shrink height
        newViewFrame.size.height -= statusBarFrame.size.height;
        if (currentOrientation == UIInterfaceOrientationPortrait) {
            // If not upside-down move down origin
            newViewFrame.origin.y += statusBarFrame.size.height;
        }
    } else { // Is landscape 
        // portrait shrink width
        newViewFrame.size.width -= statusBarFrame.size.width;
        if (currentOrientation == UIInterfaceOrientationLandscapeLeft) {
            // If the status bar is on the left side of the window move origin
            newViewFrame.origin.x += statusBarFrame.size.width;
        }
    }
}
view.frame = newViewFrame; // pass new frame 
}

call method(message):

 if ([[UIApplication sharedApplication] isStatusBarHidden]) {
        [self statusBar:NO];
 } else {
        [self statusBar:YES];
 }

Solution 5

For convenience, a Swift 4 variant of @awfulcode's answer:

var statusBarHidden = false {
    didSet {
        UIView.animate(withDuration: 0.25) {
            self.setNeedsStatusBarAppearanceUpdate()
        }
    }
}

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .default
}

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
    return .fade
}

override var prefersStatusBarHidden: Bool {
    return statusBarHidden
}
Share:
29,265
hpique
Author by

hpique

iOS, Android & Mac developer. Founder of Robot Media. @hpique

Updated on June 17, 2020

Comments

  • hpique
    hpique almost 4 years

    Consider a view controller that needs to slide out (or hide) the status bar when a button is clicked.

    - (void) buttonClick:(id)sender
    {
        [[UIApplication sharedApplication] setStatusBarHidden:YES
                                                withAnimation:UIStatusBarAnimationSlide];
    }
    

    The above effectively hides the status bar, but does not resize the root view appropriately, leaving a 20 pixel gap on top.

    What I expected is the root view to expand over the space that was previously used by the status bar (animated, with the same duration than the status bar animation).

    What's the proper way of doing this?

    (I'm aware there are plenty of similar questions, but I couldn't find any about hiding the status bar on demand as opposed to hiding it to display a new view controller)

    The "brute force" approach

    Obviously, the following works...

    [[UIApplication sharedApplication] setStatusBarHidden:YES
                                            withAnimation:UIStatusBarAnimationSlide];
    [UIView animateWithDuration:0.25 animations:^{
        CGRect frame = self.view.frame;
        frame.origin.y -= 20;
        frame.size.height += 20;
        self.view.frame = frame;
    }];
    

    ...but has disadvantages:

    • Hardcodes the duration of the slide animation
    • Hardcodes the height of the status bar
    • The root view origin stays at (0,-20). I like my frames to start at (0,0) whenever possible.

    What I already tried

    • Made sure the autoresize mask of the root view has UIViewAutoresizingFlexibleTopMargin and UIViewAutoresizingFlexibleHeight.
    • Called [self.view setNeedsLayout] after hiding the status bar.
    • Called [self.view setNeedsDisplay] after hiding the status bar.
    • Set wantsFullScreenLayout to YES before and after hiding the status bar.