UIPresentationController - Detecting if We Are Presented as Popover
As we’re finally dropping iOS 7 in PSPDFKit, I’ve lead the project to migrate everything to Apple’s new wonderful
UIPresentationController API. Now
UIPresentationController is by far not perfect, but it’s a huge step forward coming from
UIPopoverController and makes presenting in mixed environments much simpler and cleaner. The beauty: The view controller hierarchy is now correct, where in the older popover controller there were unconnected gaps, and it wasn’t easy to go from the
contentViewController to the parent presenter. This required inconvenient redirects when a controller inside a popover wanted to dismiss itself. RIP,
UIPopoverController. You won’t be missed.
Detecting if we are in a popover
Sometimes it’s quite useful to know inside the view controller if we are presented as a popover or modally. Of course things like a close button should be managed by the presenter, not the presented object, however there might be other changes (keyboard specifics, moving bar buttons around to have space for the close button, sizing differences). These cannot be determined by the trait classes alone. Initially I was naive and just checked for the existence of
.popoverPresentationController - but since the system is adaptive, this object will also be there when we are in a compact trait collection. Turns out, it’s quite hard to correctly detect if we are presented as a popover. Here’s my first attempt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
The evil detail is that
UIViewController lazily creates a new
_UIFullscreenPresentationController, so simply checking for
.presentationController is not always correct, as it doesn’t include the case where we are embedded as a child view controller. Now you might think I’m doing it wrong, but there are some valid cases for this. (We have a container that basically embeds other view controllers completely and just adds a switch control as top bar).
Turns out, Apple has the same problem, since after building all of that I found the (of course!) private
_isInPopoverPresentation (click for rage-tweet). Now this is interesting - let’s see how they detect this. (Code approximated as I don’t have access to UIKit’s source code)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
The Problem: Apple uses
_originalPresentationController for the detection, but we only have
presentationController available - which unfortunately here lazily creates a presentation controller when first being accessed. The above is also incorrect - we have a case where we embed a controller full-screen inside another controller, and this code assumes YES, but doesn’t crawl up the parent/child view controller hierarchy, so we end up with a situation where being in a popover is assumed when it’s clearly not:
When we use the above code, everything works:
Notice how we change the background so the popover uses transparency, while modally we use a clear white. The close button is handled externally by the presentation logic. The solution we use is also not 100% correct - cases where we would present inside the context of a popover would likely be incorrectly flagged as modal presentation - but since we don’t use any of that I didn’t investigate further.
Going back, how did we do things before presentation controllers? In PSPDFKit we set a flag on the view controller, however this was prone to error. What Apple and many others are doing is simply looking at the view hierarchy if there is a
_UIPopoverView in the tree . This is a used and proven method, and something Apple uses a lot in UIKit as well, and which has quite some usage and similar implementations inside UIKit. (
[UITextField _inPopover], …)
Looking at the arrow direction
Someone from Twitter pointed out that there’s even another way to detect if the
UIPopoverPresentationController is really used to present as popover, as we can look at the
arrowDirection - if that is set to
UIPopoverArrowDirectionUnknown it’s likely modal and not a popover.
Are there even better ways? Am I missing something here? I’m not happy with any of the above solutions. Ideally I should be able to ask the controller what his or the nearest ancestor’s adapted current modal presentation is. I’ve filed rdar://22048335 for an API extension - maybe we get this in iOS 10.