Setting device orientation in Swift iOS
Asked Answered
C

17

68

I am working on a swift app for iPhone. There is a modal view in my application that I want only to be in portrait view.

My question is, how do I programmatically force the phone to not allow rotation? In other words, I am looking for code that will not allow a modal view to be displayed in landscape mode (turning on portrait rotation lock).

This is just for 1 modal view, so I can't turn off rotation for the entire app, otherwise I would just disable rotation altogether.

I found code in my research here But it is in objective C, in case that helps. Thanks!

Crampon answered 3/9, 2014 at 19:11 Comment(1)
This is an answer that resolve your problem and others related: #37984346Rentschler
W
52

You can paste these methods in the ViewController of each view that needs to be portrait:

override func shouldAutorotate() -> Bool {
    return false
}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}
Weaponless answered 3/9, 2014 at 20:54 Comment(11)
This code doesn't work if the device is in landscape mode when the modal view is called. In those circumstances, the modal view is displayed in landscape mode, and can not be rotated out. What shoud I do? Thanks for the answer, though, and I'll accept it if we can get this sorted out.Crampon
That what I thought the latter method of the two above did. I'll look for more ways to accomplish this, but I can't test it because I've been haing trouble running iOS simulator and I am not part of the iOS developer program.Weaponless
Edited to cast UIInterfaceOrientation to Int. It's a UInt value and the current version of Swift now requires the cast.Gaziantep
This shows how to default load landscape. #27038339Sheikh
And I don't believe this works if you have a VC within a Navigation controller, if you only want it to apply to one VC and not all.Sheikh
@user1021430 - someone came in after me and changed it to .rawValue.Gaziantep
Thanks. This answer is incorrect, then. It will not compile. DaRk-_-D0G's answer below has the correct call to both .rawValue AND casting it to Int.Sanbenito
Should be using UIInterfaceOrientationMask instead of UIInterfaceOrientationPersnickety
you should return true from shouldAutorotate().Sharlasharleen
anyone knows how to apply landscape orientation to only view controller, rest all view should be in portrait orientation, when working with navigation controller?Ngo
Does not work in Swift 3. Also, in Swift 3 shouldAutorotate and supportedInterfaceOrientations are no longer functions but variables instead.Littlest
N
71

for LandscapeLeft and LandscapeRight (Update Swift 2.0)

enter image description here And you have this in info

enter image description here

And UIController

override func shouldAutorotate() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return [UIInterfaceOrientationMask.LandscapeLeft,UIInterfaceOrientationMask.LandscapeRight]
}

For PortraitUpsideDown and Portrait use that enter image description here

override func shouldAutorotate() -> Bool {
    if (UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft ||
        UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight ||
        UIDevice.currentDevice().orientation == UIDeviceOrientation.Unknown) {
            return false
    }
    else {
        return true
    }
}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return [UIInterfaceOrientationMask.Portrait ,UIInterfaceOrientationMask.PortraitUpsideDown]
}

Other solution:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        if visibleViewController is MyViewController {
            return true   // rotation
        } else {
            return false  // no rotation
        }
    }
    
    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return (visibleViewController?.supportedInterfaceOrientations())!
    }
}
Newsprint answered 24/12, 2014 at 23:52 Comment(3)
Nice find for iPad orientation in the infoInvective
@dark-_-d0g in the last one should it not be true in the first statement and false in the second? maybe a cut and paste typo...Risky
I think for Portrait view and case of UIDeviceOrientation.Unknown shouldRotate() should return true. In my tests if the iPad is in Landscape mode when the app is started, the app stays in this mode.Discommodity
W
52

You can paste these methods in the ViewController of each view that needs to be portrait:

override func shouldAutorotate() -> Bool {
    return false
}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait
}
Weaponless answered 3/9, 2014 at 20:54 Comment(11)
This code doesn't work if the device is in landscape mode when the modal view is called. In those circumstances, the modal view is displayed in landscape mode, and can not be rotated out. What shoud I do? Thanks for the answer, though, and I'll accept it if we can get this sorted out.Crampon
That what I thought the latter method of the two above did. I'll look for more ways to accomplish this, but I can't test it because I've been haing trouble running iOS simulator and I am not part of the iOS developer program.Weaponless
Edited to cast UIInterfaceOrientation to Int. It's a UInt value and the current version of Swift now requires the cast.Gaziantep
This shows how to default load landscape. #27038339Sheikh
And I don't believe this works if you have a VC within a Navigation controller, if you only want it to apply to one VC and not all.Sheikh
@user1021430 - someone came in after me and changed it to .rawValue.Gaziantep
Thanks. This answer is incorrect, then. It will not compile. DaRk-_-D0G's answer below has the correct call to both .rawValue AND casting it to Int.Sanbenito
Should be using UIInterfaceOrientationMask instead of UIInterfaceOrientationPersnickety
you should return true from shouldAutorotate().Sharlasharleen
anyone knows how to apply landscape orientation to only view controller, rest all view should be in portrait orientation, when working with navigation controller?Ngo
Does not work in Swift 3. Also, in Swift 3 shouldAutorotate and supportedInterfaceOrientations are no longer functions but variables instead.Littlest
T
43

Swift 3

Orientation rotation is more complicated if a view controller is embedded in UINavigationController or UITabBarController the navigation or tab bar controller takes precedence and makes the decisions on autorotation and supported orientations.

Use the following extensions on UINavigationController and UITabBarController so that view controllers that are embedded in one of these controllers get to make the decisions:

UINavigationController extension

extension UINavigationController {

override open var shouldAutorotate: Bool {
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation{
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.preferredInterfaceOrientationForPresentation
        }
        return super.preferredInterfaceOrientationForPresentation
    }
}

override open var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }
 }}

UITabBarController extension

extension UITabBarController {

override open var shouldAutorotate: Bool {
    get {
        if let selectedVC = selectedViewController{
            return selectedVC.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation{
    get {
        if let selectedVC = selectedViewController{
            return selectedVC.preferredInterfaceOrientationForPresentation
        }
        return super.preferredInterfaceOrientationForPresentation
    }
}

override open var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    get {
        if let selectedVC = selectedViewController{
            return selectedVC.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }
}}

Now you can override the supportedInterfaceOrientations, shouldAutoRotate and preferredInterfaceOrientationForPresentation in the view controller you want to lock down otherwise you can leave out the overrides in other view controllers that you want to inherit the default orientation behavior specified in your app's plist.

Lock to Specific Orientation

class YourViewController: UIViewController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    get {
        return .portrait
    }
}}

Disable Rotation

    class YourViewController: UIViewController {
open override var shouldAutorotate: Bool {
    get {
        return false
    }
}}

Change Preferred Interface Orientation For Presentation

class YourViewController: UIViewController {
open override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation{
    get {
        return .portrait
    }
}}
Telemann answered 24/9, 2016 at 9:11 Comment(1)
Its not working for swift 4, showing "override' can only be specified on class members"Mispronounce
K
18

Above code might not be working due to possibility if your view controller belongs to a navigation controller. If yes then it has to obey the rules of the navigation controller even if it has different orientation rules itself. A better approach would be to let the view controller decide for itself and the navigation controller will use the decision of the top most view controller.

We can support both locking to current orientation and autorotating to lock on a specific orientation with this generic extension on UINavigationController: -:

extension UINavigationController {
            public override func shouldAutorotate() -> Bool {
                return visibleViewController.shouldAutorotate()
            }

        public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
            return (visibleViewController?.supportedInterfaceOrientations())!
        }
    }

Now inside your view controller we can

class ViewController: UIViewController {
    // MARK: Autoroate configuration

    override func shouldAutorotate() -> Bool {
        if (UIDevice.currentDevice().orientation == UIDeviceOrientation.Portrait ||
            UIDevice.currentDevice().orientation == UIDeviceOrientation.PortraitUpsideDown ||
            UIDevice.currentDevice().orientation == UIDeviceOrientation.Unknown) {
                return true
        }
        else {
            return false
        }
    }

    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Portrait.rawValue) | Int(UIInterfaceOrientationMask.PortraitUpsideDown.rawValue)
    }
}

Hope it helps. Thanks

Kelcie answered 26/6, 2015 at 13:5 Comment(2)
I'm sure you're right! but... I made a navigationController swift file and put in the extension. I put print's in and found that it is called only first time through. The normal movement between screens does not call the functions in the extension. .Unship
Hi @Vivek, I used your code and it's working fine. But i don't understand some of my view controllers are rotating and some are fine. Do u have any idea??Benzyl
B
11

This will disable autorotation of the view:

override func shouldAutorotate() -> Bool {
    return false;
}

Update

override func shouldAutorotate() -> Bool {
    if (UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeLeft ||
        UIDevice.currentDevice().orientation == UIDeviceOrientation.LandscapeRight ||
        UIDevice.currentDevice().orientation == UIDeviceOrientation.Unknown) {
            return false;
    }
    else {
        return true;
    }
}

If app is in landscape mode and you show a view which must be showed in portrait mode, this will allow app to change it's orientation to portrait (of course when device will be rotated to such orientation).

Biogenesis answered 3/9, 2014 at 20:32 Comment(0)
P
11

If someone wants the answer, I think I just got it. Try this:

  • Go to your .plist file and check all the orientations.
  • In the view controller you want to force orientation, add the following code:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.Portrait.toRaw().hashValue | UIInterfaceOrientationMask.PortraitUpsideDown.toRaw().hashValue
}

Hope it helps !

EDIT :

To force rotation, use the following code :

let value = UIInterfaceOrientation.LandscapeRight.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

It works for iOS 7 & 8 !

Psid answered 30/9, 2014 at 9:26 Comment(5)
I had to replace ".toRaw()" with ".rawValue". Still not working for me though :/Sheikh
UIDevice.currentDevice().setValue(value, forKey: "orientation") would likely get your app rejected at the App Store. You are not allowed to set the device's orientation like this.Leander
Shouldn't we use UIDeviceOrientation enum instead of UIInterfaceOrientation when we set the orientation key of the device? As the enums do not have the same values, mixing them up is dangerous I guess.Microphyte
what would be the correct way to do this then? I need to turn my screen orientation when a user taps an button.Coir
does not work; error: Cannot assign to property: 'orientation' is a get-only propertyTintoretto
D
9

enter image description here

Go to your pList and add or remove the following as per your requirement:

"Supported Interface Orientations" - Array
"Portrait (bottom home button)" - String
"Portrait (top home button)" - String
"Supported Interface Orientations (iPad)" - Array
"Portrait (bottom home button)" - String
"Portrait (top home button)" - String
"Landscape (left home button)" - String
"Landscape (right home button)" - String

Note: This method allows rotation for a entire app.

OR

Make a ParentViewController for UIViewControllers in a project (Inheritance Method).

//  UIappViewController.swift

import UIKit

class UIappViewController: UIViewController {
          super.viewDidLoad()   
    }
//Making methods to lock Device orientation.
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
    override func shouldAutorotate() -> Bool {
        return false
    }                                                                                                                                       
    override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }

Associate every view controller's parent controller as UIappViewController.

//  LoginViewController.swift

import UIKit
import Foundation

class LoginViewController: UIappViewController{

    override func viewDidLoad()
    {
        super.viewDidLoad()

    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
Deposition answered 22/10, 2015 at 12:46 Comment(0)
P
8

For Swift 3, iOS 10

override open var shouldAutorotate: Bool {
    return false
}

override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
}

override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return .portrait
}

However, there is a bug with the setting shouldAutorotate doesn't work on iOS 9 currently.

Paginal answered 18/11, 2016 at 22:46 Comment(0)
S
6

In info.plist file , change the orientations which you want in "supported interface orientation".

In swift the way supporting files->info.plist->supporting interface orientation.

Schrimsher answered 16/12, 2014 at 5:40 Comment(0)
B
4

I've been struggling all morning to get ONLY landscape left/right supported properly. I discovered something really annoying; although the "General" tab allows you to deselect "Portrait" for device orientation, you have to edit the plist itself to disable Portrait and PortraitUpsideDown INTERFACE orientations - it's the last key in the plist: "Supported Interface Orientations".

The other thing is that it seems you must use the "mask" versions of the enums (e.g., UIInterfaceOrientationMask.LandscapeLeft), not just the orientation one. The code that got it working for me (in my main viewController):

override func shouldAutorotate() -> Bool {
    return true
}

override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.LandscapeLeft.rawValue) | Int(UIInterfaceOrientationMask.LandscapeRight.rawValue)
}

Making this combination of plist changes and code is the only way I've been able to get it working properly.

Bulbil answered 3/12, 2014 at 19:8 Comment(0)
A
4

More Swift-like version:

override func shouldAutorotate() -> Bool {
    switch UIDevice.currentDevice().orientation {
    case .Portrait, .PortraitUpsideDown, .Unknown:
        return true
    default:
        return false
    }
}

override func supportedInterfaceOrientations() -> Int {
    return Int(UIInterfaceOrientationMask.Portrait.rawValue) | Int(UIInterfaceOrientationMask.PortraitUpsideDown.rawValue)
}

UINavigationController from Vivek Parihar

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}
Abrams answered 19/8, 2015 at 9:1 Comment(0)
M
3

// Swift 2

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    let orientation: UIInterfaceOrientationMask =
    [UIInterfaceOrientationMask.Portrait, UIInterfaceOrientationMask.PortraitUpsideDown]
    return orientation
}
Megilp answered 1/12, 2015 at 19:46 Comment(1)
After going through many of the options, this one from @Leo is the only one that worked for me. I'm on Xcode 7.3, IOS 9.3, and Swift 2.2Secretin
A
2

Two suggestions with @Vivek Parihar's solution :

  1. If we are presenting any viewController we should check nil for “visibleViewController” in navigationController extension

    extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        var shouldAutorotate = false
        if visibleViewController != nil {
            shouldAutorotate = visibleViewController.shouldAutorotate()
        }
        return shouldAutorotate
    }
    
    public override func supportedInterfaceOrientations() -> Int {
        return visibleViewController.supportedInterfaceOrientations()
    }
    }
    
  2. If We are using any action sheet to present and user will rotate upsideDown, Your action sheet will open from top edge of the screen :P, to solve this, we should take Portrait only

    override func shouldAutorotate() -> Bool {
    if (UIDevice.currentDevice().orientation == UIDeviceOrientation.Portrait ||
        UIDevice.currentDevice().orientation == UIDeviceOrientation.Unknown) {
            return true
    }
    else {
        return false
    }
    

    }

    override func supportedInterfaceOrientations() -> Int {
        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    }
    
Archaeology answered 16/9, 2015 at 15:15 Comment(0)
S
0

Swift 2.2

    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {

    if self.window?.rootViewController?.presentedViewController is SignatureLandscapeViewController {

        let secondController = self.window!.rootViewController!.presentedViewController as! SignatureLandscapeViewController

        if secondController.isPresented {

            return UIInterfaceOrientationMask.LandscapeLeft;

        } else {

            return UIInterfaceOrientationMask.Portrait;
        }

    } else {

        return UIInterfaceOrientationMask.Portrait;
    }
}
Subtreasury answered 12/5, 2016 at 13:47 Comment(2)
Where do I have to put this? In the AppDelegate? If I do this I get the following error: 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [MyProject.MainViewController shouldAutorotate] is returning YES'Idola
Put this func in the AppDelegate. And remove taht func from your [MyProject.MainViewController shouldAutorotate] is returning YES'. thanksHeteropolar
Y
0

My humble contribution (Xcode 8, Swift 3):

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        if let rootViewController = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController) {
            if (rootViewController.responds(to: Selector(("canRotate")))) {
                // Unlock landscape view orientations for this view controller
                return .allButUpsideDown;
            }
        }
        return .portrait;        
    }

    private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
        if (rootViewController == nil) { return nil }
        if (rootViewController.isKind(of: (UITabBarController).self)) {
            return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
        } else if (rootViewController.isKind(of:(UINavigationController).self)) {
            return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
        } else if (rootViewController.presentedViewController != nil) {
            return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
        }
        return rootViewController
    }

... on the AppDelegate. All the credits for Gandhi Mena: http://www.jairobjunior.com/blog/2016/03/05/how-to-rotate-only-one-view-controller-to-landscape-in-ios-slash-swift/

Yehudit answered 4/2, 2017 at 3:14 Comment(0)
P
0

From ios 10.0 we need set { self.orientations = newValue } for setting up the orientation, Make sure landscape property is enabled in your project.

private var orientations = UIInterfaceOrientationMask.landscapeLeft
override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
    get { return self.orientations }
    set { self.orientations = newValue }
}
Proprietor answered 19/6, 2017 at 4:7 Comment(0)
G
0

Swift 4:

The simplest answer, in my case needing to ensure one onboarding tutorial view was portrait-only:

extension myViewController {
    //manage rotation for this viewcontroller
    override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

Eezy-peezy.

Gremlin answered 5/6, 2018 at 12:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.