tenuki
tenuki

Reputation: 370

Presenting a rotating, sheet-style modal on top of another view controller locked in portrait orientation

I have an app whose main view controller (call it VC-A) is locked in portrait orientation. I would like to present a sheet-style modal (call it VC-B) over top of it, allowing the modal to rotate freely. There are some additional constraints:

  1. VC-A should remain in portrait orientation at all times. No glimpses of or animations involving landscape orientation, even when transitioning to or from VC-B.
  2. If VC-B is presented while the device is in a landscape orientation, it should present in that orientation (slide in from the long side of the screen). It should also dismiss in this direction.
  3. In portrait orientation, VC-B should present with the default transition/animation such that VC-A is pushed down and scaled slightly.

There have been many similar questions and responses over the years, but most of the suggested answers don't meet all of these requirements, don't work with modal presentation, or are much too hacky. That said, this should definitely be possible – I have seen at least two apps with implementations that meet my requirements. Not sure if it's kosher to post existing apps here, but I can provide examples if needed.

So far, I have tried the following:

Option 1: Custom UINavigationController subclass

Something similar to the answer here, where the containing navigation controller returns a different value for supportedInterfaceOrientations depending on the topmost controller. This works but violates (1) and (2) – there is always an animation when presenting or dismissing a view controller that requires a different orientation than the previous one.

I also tried presenting the modal from a transparent nested/child view controller, but that has similar limitations.

Unwanted rotation animations when held in landscape orientation:

Option 2: Multiple UIWindows (more promising)

Instead of presenting VC-B directly from VC-A (or a common parent), create a new, transparent UIWindow and use its rootViewController to present the modal. This solves (1) and (2) beautifully:

Separate UIWindow example

However, it fails at (3) because the sheetPresentationController no longer involves VC-A. Instead, the clear rootViewController is animated, resulting in a transparent shadow on top of VC-A:

Shadow behavior in separate UIWindow

This shadow/dimming can be prevented by setting largestUndimmedDetentIdentifier to .large:

Separate window without shadow

However, I just want to apply that effect to VC-A, not eliminate it entirely. The end result should look just like it does when no separate UIWindow is involved:

The desired behavior

I can think of some potential workarounds, but I'm not really sure how to proceed. I have tried presenting a transparent view controller (call it VC-C) from VC-A at the same time as presenting VC-B from the other UIWindow. This creates the illusion that VC-B is being presented from the same view hierarchy as VC-A (by causing VC-A to have the push down effect I want), but complicates dismissal. It's trivial enough to ensure that both sheets are dismissed at the same time if dismiss() is used directly, but if the user drags down on VC-B, it seems quite difficult to get VC-C to sync with that motion.

Is there some other technique or API I'm missing? Maybe some mystery configuration of UISheetPresentationController that I've overlooked, or a clever way to use custom transitions? Some way to mirror the contents of VC-A into the other window's rootViewController? Again, this should be very possible as I have seen it happen in the wild.

Upvotes: 0

Views: 65

Answers (0)

Related Questions