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
App Store

I 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!

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀