Reputation: 7312
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
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
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(_:)
.
I got no ugly navbar or view controller jumps after that.
Upvotes: 2
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
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