Michał Ciuba Michał Ciuba - 5 months ago 329
iOS Question

How to prevent status bar from overlapping content with hidesBarsOnSwipe set on UINavigationController?

I'm trying to use the new feature added in iOS 8 - hiding the navigation bar while user is scrolling the table view (similar to what mobile Safari does). I'm setting the property

hidesBarsOnSwipe
of
UINavigationController
to
YES
in
viewDidAppear
method of
UITableViewController
:

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if([self.navigationController respondsToSelector:@selector(hidesBarsOnSwipe)]) {
self.navigationController.hidesBarsOnSwipe = YES;
}
}


The navigation bar hides when the view is being scrolled. So far so good. But the status bar is still visible and my table view contents show through it, which looks ugly:

enter image description here

I tried setting
edgesForExtendedLayout
to
UIEdgeRectNone
or adjusting the
contentInset
of the table view, but it didn't help. Is there any other solution to hide the status bar along with the navigation bar, or make it opaque?

Answer

Building off of anas' answer, I have a working solution (I'm assuming tableViewController is your UITableViewController instance):

In a UINavigationController subclass (or also potentially from tableViewController):

- (void)viewDidLoad {
    if ([self respondsToSelector:@selector(barHideOnSwipeGestureRecognizer)]) {
        // iOS 8+
        self.hidesBarsOnSwipe = YES;
        [self.barHideOnSwipeGestureRecognizer addTarget:self action:@selector(swipe:)];
    }
}

- (void)swipe:(UISwipeGestureRecognizer *)recognizer {
    BOOL shouldHideStatusBar = self.navigationController.navigationBar.frame.origin.y < 0;
    tableViewController.hideStatusBar = shouldHideStatusBar;
    [UIView animateWithDuration:0.2 animations:^{
        [tableViewController setNeedsStatusBarAppearanceUpdate];
    }];
}

In your tableViewController:

@property(nonatomic, getter = shouldHideStatusBar) BOOL hideStatusBar;

- (BOOL)prefersStatusBarHidden {
    return [self shouldHideStatusBar];
}

Let me know if this doesn't work for you. A few non-obvious things:

  • self.navigationController.navigationBar.frame.origin.y was -44 (the negative height of the navigation bar) when hidden, and 20 (the height of the status bar) when visible. There was no in-between, even during animations, so a negative value == hidden and a nonnegative value == visible.
  • The child view controller is the one queried for whether or not the status bar should be hidden. In my case, I have a UIViewController within a UINavigationController within a UITabBarController, and it didn't work until I overrode prefersStatusBarHidden on the UIViewController.
  • Since a hidden status bar has no frame, your content might jerk upwards 20 points unless you wrap the call to setNeedsStatusBarAppearanceUpdate in an animation block.
  • Hopefully the syntax is correct; I backported this from my Swift code.
Comments