Search code examples
ioscocoa-touchios9uistatusbar

iOS 9 extended status bar bug?


I'm having a problem in my app where there is a black bar at the top of app and my whole view gets pushed down 20pts from the top.

Here is a very short sample project that reproduces the bug: https://github.com/sbiermanlytle/iOS-status-bar-bug/tree/master

To reproduce:

  1. Activate the extended status bar (start a route on Google maps or turn on a hotspot)
  2. Start the demo app
  3. Navigate to the 3rd view
  4. Go back to the 2nd view, you will see the black bar at the top

How do I get rid of the black bar?

Here's a full explanation of the sample app:

There's a series of 3 view controllers, 1 launches 2, UIViewControllerBasedStatusBarAppearance is set to YES, the 1st and 3rd view controllers hide the status bar, and the 2nd one shows it.

Everything displays ok when you launch the 2nd view and 3rd view, but when you dismiss the 3rd view, there is a black bar at the top of the 2nd view that will not go away.


Solution

  • Looks like iOS bug.

    I don't know a beautiful way around this but these certainly works:


    Use deprecated API

    Change View controller-based status bar appearance entry in Info.plist to NO. Add code like this to all of your view controllers (or to common superclass, hope you have one ;) ):

    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [[UIApplication sharedApplication] setStatusBarHidden:[self prefersStatusBarHidden]];
    }
    

    Use ugly tweak

    By manually updating frame of failed system view upon causing condition. This may or may not break something outside of test app.

    In UIViewController+TheTweak.h

    #import <UIKit/UIKit.h>
    
    @interface UIViewController (TheTweak)
    
    - (void)transitionViewForceLayoutTweak;
    
    @end
    

    In UIViewController+TheTweak.m (mind the FIXME comment)

    #import "UIViewController+TheTweak.h"
    #import "UIView+TheTweak.h"
    
    @implementation UIViewController (TheTweak)
    
    - (void)transitionViewForceLayoutTweak {
        UIViewController *presenting = [self presentingViewController];
        if (([self presentedViewController] != nil) && ([self presentingViewController] != nil)) {
            if ([self prefersStatusBarHidden]) {
                
                NSUInteger howDeepDownTheStack = 0;
                do {
                    ++howDeepDownTheStack;
                    presenting = [presenting presentingViewController];
                } while (presenting != nil);
                
                //FIXME: replace with a reliable way to get window throughout whole app, without assuming it is the 'key' one. depends on your app's specifics
                UIWindow *window = [[UIApplication sharedApplication] keyWindow];
                [window forceLayoutTransitionViewsToDepth:howDeepDownTheStack];
            }
        }
    }
    
    @end
    

    In UIView+TheTweak.h

    #import <UIKit/UIKit.h>
    
    @interface UIView (TheTweak)
    
    - (void)forceLayoutTransitionViewsToDepth:(NSUInteger)depth;
    
    @end
    

    In UIView+TheTweak.m

    #import "UIView+TheTweak.h"
    
    @implementation UIView (TheTweak)
    
    - (void)forceLayoutTransitionViewsToDepth:(NSUInteger)depth {
        if (depth > 0) { //just in case
            for (UIView *childView in [self subviews]) {
                if ([NSStringFromClass([childView class]) isEqualToString:@"UITransitionView"]) {
                    childView.frame = self.bounds;
                    if (depth > 1) {
                        [childView forceLayoutTransitionViewsToDepth:(depth - 1)];
                    }
                }
            }
        }
    }
    
    @end
    

    Now, in every view controller (or in common superclass):

    #import "UIViewController+TheTweak.h"
    
    ... // whatever goes here
    
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        
        [self transitionViewForceLayoutTweak];
    }
    

    Or your can just turn background of problematic controller black :)