Maintaining Scroll Position on iPad

My most recent update to Unread included a fix for an odd bug. The steps to reproduce it were:

  1. Open Unread on an iPad.
  2. Put the iPad in landscape orientation.
  3. Scroll to the bottom of any scrollable view.
  4. Press the home button to leave the app.
  5. Open the app again.

Expected Result: When returning to the app, one would expect the scroll position to be unchanged.

Actual Result: The user would be scrolled up a few hundred points from the bottom.

When digging into this, I found that I can write a trivial app and reproduce the behavior. Here is a video of exactly that:

This only happens on the iPad, and it only happens when in landscape mode. It also only happens with apps that do not require the full iPad screen. I introduced the issue into Unread when I added Split View and Slide Over support just by removing the UIRequiresFullScreen key from Info.plist.

I can reproduce the behavior in a variety of Apple apps including Mail, Notes, App Store, Contacts, and Files. In some cases the Apple app scrolls all the way to the top instead of just a few hundred points up.

I believe this is the result of iOS generating portrait orientation thumbnails. When you leave the app, the view controller’s viewWillTransition method gets called with the portrait size and then again with the landscape size. When transitioning to a taller size while scrolled to the bottom, the scroll view has to adjust the content offset. It does not revert the scroll position change when reverting the orientation change. I filed Radar 37650722 describing the issue.

I wrote some code to fix this for Unread. The code is generic enough to work for just about any UIScrollView, so I made the source for a sample app with the fix available on GitHub.