2

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.

Cbas
  • 6,003
  • 11
  • 56
  • 87
  • 2
    In SecondViewController in buttonPushed change nav.modalTransitionStyle = UIModalPresentationPopover; to nav.modalPresentationStyle = UIModalPresentationOverCurrentContext; – beyowulf May 16 '16 at 19:18
  • Still seeing the black bar - did you test with the extended status bar active? – Cbas May 17 '16 at 17:58
  • Yes, I tested on device and in simulator (command + Y). – beyowulf May 17 '16 at 18:49

2 Answers2

1

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 :)

Community
  • 1
  • 1
gp-v
  • 114
  • 1
  • 7
  • I can't use the deprecated API b/c I ran into a problem with it earlier and had to switch to the new one. Also I can't turn the screen black b/c there's actually an image and a bottom bar that gets pushed off screen. The window hack works perfectly in the demo app though! will have to see if it messes up the layout elsewhere – Cbas May 17 '16 at 18:05
  • Just care the iOS versioning. This particular detail of view hierarchy is not documented and may or may not change one day, with a chance of breaking things. I'd recommend to fallback to 'normal' implementation as soon as Apple fixes it. – gp-v May 17 '16 at 19:51
0

Simplest tweak

@interface SecondViewController ()

@property (assign, nonatomic) BOOL statusBarHidden;

@end

@implementation SecondViewController

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  self.statusBarHidden = [UIApplication sharedApplication].statusBarHidden; //store current state
}

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  self.statusBarHidden = YES;
  [UIView animateWithDuration:0.2f animations:^{
     [self setNeedsStatusBarAppearanceUpdate];
   }];
}

- (BOOL)prefersStatusBarHidden {
    return self.statusBarHidden;
}
@end
V_O
  • 1
  • 1