on
One Way Documentation
Macoun is coming back and I am updating the App. Three years without having a deeper look at it means there is a lot to update, using modern APIs. One of the constructs that has been tricky from the beginning is the stacking of view controllers I use at places in the app: It’s an UIViewController
in an UIPageViewController
in an UINavigationController
in an UITabBarController
. While this sounds unreasonable at first, it is straight forward: The app has different entry points (the tabs), some of them showing a list of items (talks or speakers in a UITableViewController
in said UINavigationController
). If you select one of these items, the UIViewController
pushed is embedded in an UIPageViewController
so you can navigate between items on the same level by swiping left/right.
The page view controller became a problem when iOS started to extend the table view behind the navigation bar. The embedded controllers do not know about the navigation bar. I fixed it by manually correcting the contentInset
and contentOffset
in viewWillAppear
. Since iOS 15, the navigation bar has distinct appearances for when the main scroll view is at the top or when scrolled down (same for the tab bar, the toolbar and other views). Unfortunatelly, when using the transition style .scroll
, the page view controller embeds the contained view controller in an UIScrollView
. With a horizontal navigation orientation, this scroll view is always scrolled to top/bottom. That’s not something I could fix in viewWillAppear
. I had to teach the navigation bar about the correct scroll view.
I spend most evenings of one week searching for a solution. I’ve searched the documentation of UINavigationBar
, UINavigationItem
, UINavigationController
, and UIScrollView
. Also StackOverflow and tutorials. UIPageViewController
does not seem commonly used. Let alone in an navigation controller. Best I found was this thread that did not have a solution. Updating the navigation item was a common problem but I did not find a solution for how to connect the navigation bar to the correct scroll view.
I did write my own page view controller that used an UIPanGestureRecognizer
to move to the next page but that also broke the connection between the navigation bar and the child views scroll view even thou I did not use a scroll view. I nearly settled on setting scrollEdgeAppearance
to something that more or less looks like the default appearance used as standardAppearance
. Then I noticed, that the tab bar looks wrong, too. While searching how to fix that, I found tabBarObservedScrollView
– which is deprecated. Luckily, there is a note on what replaces it: setContentScrollView(_:for:)
.
In iOS 15, Apple added everything needed to override the automagical detection of the scroll view that all the different bars look at. The overview describes clearly what I was searching for:
Sets the scroll view that bars observe for the specified edge.
It’s all there: The functionality I needed, it does work (tested in iOS 16), and it is documented. Was it my fault to not find it? Maybe I should have checked UIViewController
more closely but the methods are not mentioned anywhere in the detailed overview. Searching for “navigation” or “navigationbar” does not show them. I think I did not miss the hint in any of the other classes that I looked at: None of them mentions the concept of the content scroll view. I even checked the Xcode and iOS release notes: No word about the new methods.
Documentation needs to have pointers in both directions: Obviously the documentation has to tell you what an API is good for but it is also important that there is a pointer from the place where you might need it.
My suggestion (filed as FB12082146) is to add one sentence to each class that makes use of the content scroll view:
The scroll view used is determined automatically but can be modified using
setContentScrollView(_:for:)
or by overridingcontentScrollView(for:)
.