Now back to mobile development (next to a million other things) at Kidiyo, I was underwhelmed with the lack of support from the iOS SDK to force different landscapes across an app. In this blog I share a neat solution that works (at least) from iOS 9.0 onward.
Most SO answers suggest to override
shouldAutorotate. These can be defined
UIViewControllers and on
I’d expect to be able to get it working out of the box by simply overriding
UIViewControllers and then having
UINavigationController exposing those
values from the visible view controller.
That setup was insufficient.
Let’s look at the following scenario.
- A screen
Asupports all orientations but prefers portrait
- A screen
Bonly supports landscape
- A user can go from
B’s supported orientations to landscape did not force that screen
to be shown in landscape if I’d come from
A in portrait mode. Instead, I was
kept in portrait in
B until I rotated to landscape. From then on I no longer
could rotate back to portrait, as intended from the beginning.
Clearly, the SDK didn’t take the lead on forcing the orientation despite being aware what the current view controller supports.
The way to force rotate a screen is by setting the device “orientation” value:
Forcing rotation is easy, but there are a few caveats to cover.
preferredInterfaceOrientationForPresentation is of type
UIInterfaceOrientation and that
supportedInterfaceOrientations is of type
UIInterfaceOrientationMask. The second defines the allowed range, while the
first indicates which one, within that range, is the preferred.
Having this in mind, it is now clear that
is the value to use when forcing rotation.
It would seem like a good idea to always force the orientation to be what the
preferredInterfaceOrientationForPresentation to be.
This approach works when moving from a screen with a broader set of supported orientations
than the screen we are moving to. For instance, when moving from
B in the
Such approach taints the user experience. When at the screen
B, the user holds
the device in landscape mode. By going back to
A, the screen
gets rotated to portrait, even though
A supports landscape.
The solution, then, is to force rotation only when the screen we are moving to does not support the orientation we are in already.
UIInterfaceOrientationMask is an enum and not a list, so we can’t just
check if it contains a
UIInterfaceOrientation, and it doesn’t provide any function
to do this out of the box either.
Therefore, we need to extend it:
Now, when moving to a new
UIViewController, we can check if the current
orientation supports its
preferredInterfaceOrientationForPresentation. If it
doesn’t, we have to rotate to that preferred orientation:
Bringing it all together
Now we have everything to have it working like a charm:
- The View Controllers define both their preferred and supported and orientations. You can define this in a base class instead of having to define it in every single view controller.
- The capacity to decide when to the screen needs to be rotated
The last step is to have your
UINavigationController implementing the
Et voilá! You can checkout my whole
Orientation extension and get done in no time: