How to allow landscape for single screen in your UIKit app
Controlling device orientation without modifying the Info.plist or using “top view controller”.
Published: Dec. 29, 2022 Sponsored See booksI recently had quite a fun challenge - how to allow the gallery in SwitchBuddy to work in landscape while keeping the rest of app portrait only.
There seems to be a lot of solutions which approach this from different angles. I ended up with one that seems relatively future-proof and does not require modifying Info.plist
values for supported orientations.
The solution
The main part is to return supported orientations in your AppDelegate
method. Which means we need suitable way of dynamically changing this from other parts of the app.
I have seen some protocol approaches that also relied on the infamous “topViewController” methods on UIApplication. These are fine but I have seen this abused way too many times.
Anyway, for my solution I just created global RotationSettings
object to configure whether landscape is allowed. Is this super elegant? No. Does it work? Yes. It is easy to understand and use? Also yes.
It looks like this:
class RotationSettings {
static var allowLandscape = false
}
With this in place, we can jump back to AppDelegate and return the correct orientations.
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if RotationSettings.allowLandscape {
return [.portrait, .landscapeLeft, .landscapeRight]
} else {
return .portrait
}
}
With this in place, you can freely allow landscape and it will work. I found, that sometimes the change back to portrait takes a bit of time, but you can actually force it with this line of code on any view controller:
setNeedsUpdateOfSupportedInterfaceOrientations()
Note that the above method is iOS 16+ only.
Example usage
This is quite specific to my app because I am using the great Agrume library. But I still wanted to include it to show how the above ties together:
agrume = Agrume(urls: urls, startIndex: indexPath.item, background: .blurred(.systemThinMaterial), overlayView: overlayView)
agrume?.tapBehavior = .toggleOverlayVisibility
agrume?.willDismiss = { [weak self] in
RotationSettings.allowLandscape = false
if #available(iOS 16.0, *) {
self?.setNeedsUpdateOfSupportedInterfaceOrientations()
}
}
agrume?.show(from: self)
RotationSettings.allowLandscape = true
I am allowing landscape just after the image gallery is shown. And disabling it just prior to dismiss. This correctly rotates the device back to portrait when the user exits the gallery.
Automatic landscape
If you did not like having to toggle this manually. I think creating a base view controller that toggles this settings in viewDidAppear
and viewWillDisappear
could work pretty nicely too!