Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe area insets change when hiding status bar iOS 11

Tags:

ios

In our app, we temporarily hide the status bar as part of the animation between transitioning between two screens that both need different status bar styles.

We have a percent driven animation transition which when started, hides the status bar with animation and when finish re shows the status bar.

On iOS 11 the safe area insets include the status bar height which can be variable, and when hidden the top inset of the safe area drops to 0 height. This re-adjusts all our views and has a horrible jump between view sizes.

We still want to constrain our views to the safe area since we're trying to support iPhone X.

Can we temporarily disable the change to the safe area insets when hiding the status bar?

like image 721
David Rees Avatar asked Oct 23 '17 00:10

David Rees


3 Answers

Try add 2 constraints:

1) view - superview

enter image description here

2) view - safeArea

enter image description here

like image 184
maxwell Avatar answered Oct 10 '22 03:10

maxwell


Constraints that are set to the safe area are affected by the status bar as well as the views actual location on the screen and its transform. If you always want to just apply the top (or bottom) safe area height to your view constraint, you can do this by use of a custom constraint instead.

The following constraint will automatically set its constant value to the height of the device's top safe area height, not affected by the status bar or other parameters. To use it, change the class of any constraint into this, and their constant will always be the safe area height. Note that it will not change its value when the device is rotated.

Objective-C

@interface TopSafeAreaContraint : NSLayoutConstraint

@end

@implementation TopSafeAreaContraint

- (void)awakeFromNib {
    [super awakeFromNib];

    if (@available(iOS 11.0, *)) {
        UIEdgeInsets insets = [UIApplication sharedApplication].keyWindow.safeAreaInsets;
        self.constant = MAX(insets.top, 20.0);
    } else {
        // Pre-iOS 11.0
        self.constant = 20.0;
    }
}

@end

Swift

class TopSafeAreaConstraint: NSLayoutConstraint {
    override func awakeFromNib() {
        super.awakeFromNib()
        if #available(iOS 11.0, *) {
            let insets = UIApplication.shared.keyWindow?.safeAreaInsets ?? .zero
            self.constant = max(insets.top, 20)
        } else {
            // Pre-iOS 11.0
            self.constant = 20.0
        }
    }
}
like image 42
LGP Avatar answered Oct 10 '22 05:10

LGP


I've been experiencing a similar issue so I came up with a slightly different approach. This is not a direct answer to the problem. It is a workaround which worked in my case.

I had two different view controllers, both of which must have a navigation bar (but a navigation controller is not required). The 1st view controller is presenting the 2nd one in a modal fashion. The problem is that the 2nd view controller is landscape only, which means that on iPhones with edge-to-edge displays any overrides of prefersStatusBarHidden are ignored and the system always returns true (see here).

What I did was simulate the status bar height through a custom view, and then adjust the height constraint constant in viewDidLoad(_:).

enter image description here

enter image description here

I got no ugly navbar or view controller jumps after that.

like image 23
nstein Avatar answered Oct 10 '22 05:10

nstein