David Rees
David Rees

Reputation: 7312

Safe area insets change when hiding status bar iOS 11

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?

Upvotes: 11

Views: 9259

Answers (5)

LGP
LGP

Reputation: 4333

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
        }
    }
}

Upvotes: 7

nstein
nstein

Reputation: 349

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.

Upvotes: 2

maxwell
maxwell

Reputation: 4156

Try add 2 constraints:

1) view - superview

enter image description here

2) view - safeArea

enter image description here

Upvotes: 1

DarthMike
DarthMike

Reputation: 3481

You generally want your scroll view to go under status bar (safe area) and simply adjust it's content inset, instead of laying out only inside the safe area. The adjustement is automatic for UIScrollView by default. See contentInsetAdjustmentBehavior.

If you have scroll view full view size (under safe area), hiding and showing status bar works perfectly for me, even though the safe area insets are being modified in the scroll view automatically.

Upvotes: 0

archsten
archsten

Reputation: 501

Get a reference to the safe area top constraint and try changing the constant of that constraint to adjust for the hide/show of the status bar. This works for me, though in a somewhat different situation where I set the constraint constant within the prefersStatusBarHidden method in reaction to showing/hiding a toolbar.

Upvotes: 1

Related Questions