Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController: extendedLayoutIncludesOpaqueBars and scroll view offset

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:

Navigation Inset Issue

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?

like image 832
John Wickham Avatar asked Oct 18 '17 14:10

John Wickham


Video Answer


2 Answers

As pointed out by @beebcon before, this was indeed a bug (Apple engineer confirmed on twitter) and should be fixed in iOS 11.2.

first tweet enter image description here

(I'm sorry I used images, but this way it doesn't go away in case the tweets disappear)

like image 115
JoniVR Avatar answered Oct 24 '22 12:10

JoniVR


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

like image 32
beebcon Avatar answered Oct 24 '22 12:10

beebcon