My app's view hierarchy is pretty straightforward: a UINavigationController
contains a UITableViewController
. The navigation controller's navigation bar is opaque, which causes some strange inset behavior of the table view during navigation transitions, seen here:
To remedy this, I'm setting extendedLayoutIncludesOpaqueBars
to true
on the UITableViewController
. Doing so properly extends the view beneath the navigation bar, but changes the behavior of the table view's contentOffset
in a way that I don't quite understand. With this property set to true
, the Y-value of the table view's contentOffset
reports that it is offset higher than it should be by the current height of the navigation bar (i.e. scrolling the table view 1pt, it reports that its y-offset is -63pts).
This made me think that the navigation controller was automatically managing the scroll view's contentInset
, like it does for translucent bars. But I couldn't see any evidence that the scroll view had content insets set in scrollViewDidScroll()
. Even with automaticallyAdjustsScrollViewInsets
set to false
on the table view controller, the content offset wasn't right, so it seems it's nothing to do with the insets.
Apple's documentation about extendedLayoutIncludesOpaqueBars
doesn't mention any effect on the behavior of a scroll view's content offset. Changing the contentInset
of the table view unfortunately doesn't solve this.
I tried changing the table view controller's edgesForExtendedLayout
property to force it to extend without affecting the scroll view, but it seems this property is powerless against opaque bars.
Is there some hidden behavior with extendedLayoutIncludesOpaqueBars
that borks up a scroll view's content offset? Or could this be a bug?
As pointed out by @beebcon before, this was indeed a bug (Apple engineer confirmed on twitter) and should be fixed in iOS 11.2.
(I'm sorry I used images, but this way it doesn't go away in case the tweets disappear)
Background on what's going on---
extendedLayoutIncludesOpaqueBars
essentially tells your view to behave as if the navigation bars are translucent. Your view's frame then will begin at the top of (underneath) the navigation bar, rather than at the bottom of the bar.
The navigation controller then sees that you have a scroll view, and automatically insets it to compensate for the area covered by the bar.
In iOS 10, you could query contentInset
and see the top = 64
inset. However, in iOS 11, contentInset
is strictly for any custom insets you control -- you should use adjustedContentInset
, which is the sum total of your custom insets and any safe area insets.
adjustedContentInset -
The insets derived from the content insets and the safe area of the scroll view.
contentInset -
The custom distance that the content view is inset from the safe area or scroll view edges.
So really, the y offset of -63 makes sense, it's the same thing you'd see if your bars were translucent.
The issue you're talking about, which your GIF demonstrates, is I think a bug (see https://stackoverflow.com/a/46397291/2145198).
While JoniVR's answer should work, I solved it in my projects by setting extendedLayoutIncludesOpaqueBars = true
.
Though it's probably not a big deal either way you solve it, extending the layout feels like better practice to me, rather than changing the contentInsetAdjustmentBehavior
. Setting that to never
has a wider range of potential effects; you're telling it to never care about safe area, no matter where the safe area is coming from. Safe area could change (like on rotation) or if your controller is presented in different contexts / containers (like tab bar or not).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With