Rotation only in one ViewController
I'd recommend using supportedInterfaceOrientationsForWindow
in your appDelegate
to allow rotation only in that specific view controller, ex:
Swift 4/Swift 5
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { // Make sure the root controller has been set // (won't initially be set when the app is launched) if let navigationController = self.window?.rootViewController as? UINavigationController { // If the visible view controller is the // view controller you'd like to rotate, allow // that window to support all orientations if navigationController.visibleViewController is SpecificViewController { return UIInterfaceOrientationMask.all } // Else only allow the window to support portrait orientation else { return UIInterfaceOrientationMask.portrait } } // If the root view controller hasn't been set yet, just // return anything return UIInterfaceOrientationMask.portrait}
Note that if that SpecificViewController
is in landscape before going to a portrait screen, the other view will still open in landscape. To circumvent this, I'd recommend disallowing transitions while that view is in landscape.
Swift 3
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int { // Make sure the root controller has been set // (won't initially be set when the app is launched) if let navigationController = self.window?.rootViewController as? UINavigationController { // If the visible view controller is the // view controller you'd like to rotate, allow // that window to support all orientations if navigationController.visibleViewController is SpecificViewController { return Int(UIInterfaceOrientationMask.All.rawValue) } // Else only allow the window to support portrait orientation else { return Int(UIInterfaceOrientationMask.Portrait.rawValue) } } // If the root view controller hasn't been set yet, just // return anything return Int(UIInterfaceOrientationMask.Portrait.rawValue)}
You can also do it in a protocol oriented way.Just create the protocol
protocol CanRotate {}
Add the the same 2 methods in the AppDelegate in a more "swifty" way
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if topViewController(in: window?.rootViewController) is CanRotate { return .allButUpsideDown } else { return .portrait }}func topViewController(in rootViewController: UIViewController?) -> UIViewController? { guard let rootViewController = rootViewController else { return nil } if let tabBarController = rootViewController as? UITabBarController { return topViewController(in: tabBarController.selectedViewController) } else if let navigationController = rootViewController as? UINavigationController { return topViewController(in: navigationController.visibleViewController) } else if let presentedViewController = rootViewController.presentedViewController { return topViewController(in: presentedViewController) } return rootViewController}
And in every ViewController that you want a different behaviour, just add the protocol name in the definition of the class.
class ViewController: UIViewController, CanRotate {}
If you want any particular combination, they you can add to the protocol a variable to override
protocol CanRotate { var supportedInterfaceOrientations: UIInterfaceOrientationMask}
Sometimes when you're using a custom navigation flow (that may get really complex) the above-mentioned solutions may not always work. Besides, if you have several ViewControllers that need support for multiple orientations it may get quite tedious.
Here's a rather quick solution I found. Define a class OrientationManager
and use it to update supported orientations in AppDelegate:
class OrientationManager { static var landscapeSupported: Bool = false}
Then in AppDelegate put the orientations you want for that specific case:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { if OrientationManager.landscapeSupported { return .allButUpsideDown } return .portrait }
Then in the ViewControllers that you want to have multiple navigations update the OrientationManager
:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) OrientationManager.landscapeSupported = true}
Also, don't forget to update it once again when you'll be exiting this ViewController:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) OrientationManager.landscapeSupported = false //The code below will automatically rotate your device's orientation when you exit this ViewController let orientationValue = UIInterfaceOrientation.portrait.rawValue UIDevice.current.setValue(orientationValue, forKey: "orientation")}
Hope this helps!
Update:
You may just want to add a static func
to your Orientation Support Manager
class:
static func setOrientation(_ orientation: UIInterfaceOrientation) { let orientationValue = orientation.rawValue UIDevice.current.setValue(orientationValue, forKey: "orientation") landscapeSupported = orientation.isLandscape }
Then you can call this function whenever you need to set the orientation back to portrait. That will also update the static landscapeSupported
value:
OSM.setOrientation(.portrait)