67

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!

Community
  • 1
  • 1
rocket101
  • 7,369
  • 11
  • 45
  • 64
  • This is an answer that resolve your problem and others related: http://stackoverflow.com/questions/37983846/changing-orientations-programatically-does-not-work-fine/43054291#43054291 – Ariel Antonio Fundora Mar 27 '17 at 19:12

17 Answers17

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())!
    }
}
VLAZ
  • 26,331
  • 9
  • 49
  • 67
YanSte
  • 10,661
  • 3
  • 57
  • 53
  • Nice find for iPad orientation in the info – Phil Hudson Feb 17 '16 at 14:34
  • @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... – Juan Boero Mar 06 '16 at 00:37
  • 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. – Dmitry Yudakov Jul 26 '16 at 11:39
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
}
DrOverbuild
  • 1,245
  • 10
  • 11
  • 13
    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. – rocket101 Sep 06 '14 at 17:40
  • 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. – DrOverbuild Sep 07 '14 at 13:37
  • 2
    Edited to cast `UIInterfaceOrientation` to `Int`. It's a `UInt` value and the current version of Swift now requires the cast. – Bill Weinman Feb 06 '15 at 00:02
  • This shows how to default load landscape. http://stackoverflow.com/questions/27037839/force-landscape-mode-in-one-viewcontroller-using-swift – deebs Feb 12 '15 at 15:03
  • 4
    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. – deebs Feb 12 '15 at 15:05
  • @user1021430 - someone came in after me and changed it to `.rawValue`. – Bill Weinman May 11 '15 at 02:26
  • 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. – jeremywhuff May 12 '15 at 00:54
  • 7
    Should be using `UIInterfaceOrientationMask` instead of `UIInterfaceOrientation` – redent84 May 22 '15 at 10:10
  • 3
    you should return `true` from `shouldAutorotate()`. – AydinAngouti May 05 '16 at 23:17
  • anyone knows how to apply landscape orientation to only view controller, rest all view should be in portrait orientation, when working with navigation controller? – chetan panchal Jul 05 '17 at 14:29
  • 1
    Does not work in Swift 3. Also, in Swift 3 shouldAutorotate and supportedInterfaceOrientations are no longer functions but variables instead. – Chris Balavessov Sep 19 '17 at 10:32
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
    }
}}
Dragos
  • 1,050
  • 16
  • 21
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

Arunabh Das
  • 13,212
  • 21
  • 86
  • 109
Vivek Parihar
  • 2,318
  • 18
  • 18
  • 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. . – user462990 Feb 16 '16 at 12:15
  • 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?? – Ramakrishna Apr 24 '17 at 14:19
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).

some_id
  • 29,466
  • 62
  • 182
  • 304
nsinvocation
  • 7,559
  • 3
  • 41
  • 46
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 !

Dani Pralea
  • 4,545
  • 2
  • 31
  • 49
lobodart
  • 216
  • 1
  • 11
  • 1
    I had to replace ".toRaw()" with ".rawValue". Still not working for me though :/ – deebs Feb 12 '15 at 14:44
  • 1
    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. – emresancaktar Apr 03 '16 at 12:46
  • 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. – vcattin Apr 27 '16 at 09:50
  • what would be the correct way to do this then? I need to turn my screen orientation when a user taps an button. – user7097242 Mar 20 '18 at 20:00
  • does not work; error: Cannot assign to property: 'orientation' is a get-only property – Corbin Miller May 22 '19 at 00:05
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()
    }
Alvin George
  • 14,148
  • 92
  • 64
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.

CodeOverRide
  • 4,431
  • 43
  • 36
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.

Malu
  • 61
  • 1
  • 1
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.

jbm
  • 1,248
  • 10
  • 22
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()
    }
}
Vojtech Vrbka
  • 5,342
  • 6
  • 44
  • 63
3

// Swift 2

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    let orientation: UIInterfaceOrientationMask =
    [UIInterfaceOrientationMask.Portrait, UIInterfaceOrientationMask.PortraitUpsideDown]
    return orientation
}
Leo
  • 3,003
  • 5
  • 38
  • 61
user3561494
  • 2,164
  • 1
  • 20
  • 33
  • 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.2 – Chaim Friedman Mar 23 '16 at 16:03
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)
    }
    
Nitesh
  • 1,924
  • 21
  • 31
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;
    }
}
idris yıldız
  • 2,097
  • 20
  • 22
  • 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' – user3143691 May 12 '16 at 20:18
  • Put this func in the AppDelegate. And remove taht func from your [MyProject.MainViewController shouldAutorotate] is returning YES'. thanks – idris yıldız May 13 '16 at 06:42
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/

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 }
}
Jack
  • 13,571
  • 6
  • 76
  • 98
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.

drew..
  • 3,234
  • 3
  • 16
  • 19