Force landscape mode in one ViewController using Swift

IosSwiftUiviewcontrollerLandscape

Ios Problem Overview


I am trying to force only one view in my application on landscape mode, I am calling:

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

override func supportedInterfaceOrientations() -> Int {
    print("supportedInterfaceOrientations")
    return Int(UIInterfaceOrientationMask.LandscapeLeft.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return UIInterfaceOrientation.LandscapeLeft
}

The view is launched in the portrait mode, and keep rotating when I change the device orientation. The shouldAutorotate() method is never called.

Any help would be appreciated.

Ios Solutions


Solution 1 - Ios

It may be useful for others, I found a way to force the view to launch in landscape mode:

Put this in the viewDidLoad():

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")

and,

override var shouldAutorotate: Bool {
    return true
}

Solution 2 - Ios

Swift 4

override func viewDidLoad() {
	super.viewDidLoad()
	let value = UIInterfaceOrientation.landscapeLeft.rawValue
    UIDevice.current.setValue(value, forKey: "orientation")
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .landscapeLeft
}

override var shouldAutorotate: Bool {
    return true
}

If your view is embedded in a navigation controller, the above alone won't work. You have to cascade up by the following extension after the class definition.

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
    }
}}


Swift 3

override func viewDidLoad() {
	super.viewDidLoad()
	let value = UIInterfaceOrientation.landscapeLeft.rawValue
    UIDevice.current.setValue(value, forKey: "orientation")
}

private func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
	return UIInterfaceOrientationMask.landscapeLeft
}

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

Solution 3 - Ios

Swift 4 , Tested in iOS 11

You can specify the orientation in projectTarget -> General -> DeploymentInfo(Device Orientation) -> Portrait (Landscapeleft and Landscaperight are optional)

AppDelegate

    var myOrientation: UIInterfaceOrientationMask = .portrait
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return myOrientation
    }

LandScpaeViewController

override func viewDidLoad() {
        super.viewDidLoad()
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.myOrientation = .landscape
}

OnDismissButtonTap

let appDelegate = UIApplication.shared.delegate as! AppDelegate
 appDelegate.myOrientation = .portrait

Thats it. :)

Solution 4 - Ios

Using Swift 2.2

Try:

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

Followed By:

UIViewController.attemptRotationToDeviceOrientation()

From Apple's UIViewController Class Reference:

> Some view controllers may want to use app-specific conditions to determine what interface orientations are supported. If your view controller does this, when those conditions change, your app should call this class method. The system immediately attempts to rotate to the new orientation.

Then, as others have suggested, override the following methods as appropriate:

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.LandscapeLeft
}

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

I was having a similar issue with a signature view and this solved it for me.

Solution 5 - Ios

In AppDelegate add this:

//Orientation Variables
var myOrientation: UIInterfaceOrientationMask = .portrait
    
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return myOrientation  
}

Add this in viewController, that want to change orientation:

override func viewDidLoad() {
        super.viewDidLoad()
        self.rotateToLandsScapeDevice()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.rotateToPotraitScapeDevice()
    }
    
    func rotateToLandsScapeDevice(){
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.myOrientation = .landscapeLeft
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
        UIView.setAnimationsEnabled(true)
    }
    
    func rotateToPotraitScapeDevice(){
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        appDelegate.myOrientation = .portrait
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
        UIView.setAnimationsEnabled(true)
    }

Solution 6 - Ios

For me, the best results came from combining Zeesha's answer and sazz's answer.

Add the following lines to AppDelegate.swift:

var orientationLock = UIInterfaceOrientationMask.portrait
var myOrientation: UIInterfaceOrientationMask = .portrait
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return myOrientation
}  

Add the following line to your view controller class:

let appDel = UIApplication.shared.delegate as! AppDelegate

Add the following lines to your view controller's viewDidLoad():

appDel.myOrientation = .landscape
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")

(optional) Add this line to your dismiss function:

appDel.myOrientation = .portrait
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")

What these lines of code do is set the default orientation to portrait, rotate it landscape when the view controller loads, and then finally reset it back to portrait once the view controller closes.

Solution 7 - Ios

Overwrite (in ViewController):

override public var shouldAutorotate: Bool {
    return false
} 

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .landscapeRight
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return .landscapeRight
}

Hint for ios 13. As of ios 13, VC has different modalPresentationStyle as .automatic and device present modal view instead of Full-Screen VC. To fix this one must set modalPresentationStyle to .fullScreen. Example:

let viewController = YourViewController()
viewController.modalPresentationStyle = .fullScreen

Solution 8 - Ios

I needed to force one controller into portrait orientation. Adding this worked for me.

swift 4 with iOS 11

override var   supportedInterfaceOrientations : UIInterfaceOrientationMask{

    return  .portrait

}

Solution 9 - Ios

Works in Swift 2.2

 func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if self.window?.rootViewController?.presentedViewController is SignatureViewController {
        
        let secondController = self.window!.rootViewController!.presentedViewController as! SignatureViewController
        
        if secondController.isPresented {
            
            return UIInterfaceOrientationMask.LandscapeLeft;
            
        } else {
            
            return UIInterfaceOrientationMask.Portrait;
        }
        
    } else {
        
        return UIInterfaceOrientationMask.Portrait;
    }
}

Solution 10 - Ios

Swift 3. This locks the orientation each time the user re-opens the app.

class MyViewController: UIViewController {
	...
	override func viewDidLoad() {
		super.viewDidLoad()

        // Receive notification when app is brought to foreground
		NotificationCenter.default.addObserver(self, selector: #selector(self.onDidBecomeActive), name: NSNotification.Name.UIApplicationDidBecomeActive, object: nil)
	}
	
	// Handle notification
	func onDidBecomeActive() {
		setOrientationLandscape()
	}
	
	// Change orientation to landscape
	private func setOrientationLandscape() {
		if !UIDevice.current.orientation.isLandscape {
			let value = UIInterfaceOrientation.landscapeLeft.rawValue
			UIDevice.current.setValue(value, forKey:"orientation")
			UIViewController.attemptRotationToDeviceOrientation()
		}
	}
	
	// Only allow landscape left
	override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
		return UIInterfaceOrientationMask.landscapeLeft
	}
	
    /*
	// Allow rotation - this seems unnecessary
	private func shouldAutoRotate() -> Bool {
		return true
	}
    */
	...
}

Solution 11 - Ios

Swift 4

Trying to keep the orientation nothing worked but this for me:

...        
override func viewDidLoad() {
       super.viewDidLoad()
       forcelandscapeRight()
       let notificationCenter = NotificationCenter.default
       notificationCenter.addObserver(self, selector: #selector(forcelandscapeRight), name: Notification.Name.UIDeviceOrientationDidChange, object: nil)
    }

    @objc func forcelandscapeRight() {
        let value = UIInterfaceOrientation.landscapeRight.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
    }
....

Solution 12 - Ios

In ViewController in viewDidLoad Method call below function

func rotateDevice(){
    UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
    UIView.setAnimationsEnabled(true) // while rotating device it will perform the rotation animation
}`

> App Delegate File Add Below Function & Variables

//Orientation Variables
var orientationLock = UIInterfaceOrientationMask.portrait
var myOrientation: UIInterfaceOrientationMask = .portrait

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return .landscape }

Solution 13 - Ios

I faced a similar issue in my project. It only has support for portrait. The ViewController structure is that, Navigation contained a controller (I called A), and a long Scrollview in A controller. I need A(portrait) present to B(landscape right).

In the beginning I tried the method below and it seemed to work but eventually I found a bug in it.

Swift 5 & iOS12

// In B controller just override three properties

override var shouldAutorotate: Bool {
    return false
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.landscapeRight
}
    
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return .landscapeRight
}

And then something become strange. When controller B dismiss to controller A. The ScrollView in controller A has been slid some point.

So I used another method, so I rotate the screen when viewWillAppear. You can see the code for that below.

// In controller B
// not need override shouldAutorotate , supportedInterfaceOrientations , preferredInterfaceOrientationForPresentation

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let appDel = UIApplication.shared.delegate as! AppDelegate
    appDel.currentOrientation = .landscapeRight
    UIDevice.current.setValue( UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
}

//in viewWillDisappear rotate to portrait can not fix the bug
    

override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
    let appDel = UIApplication.shared.delegate as! AppDelegate
    appDel.currentOrientation = .portrait
    UIDevice.current.setValue( UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation() //must call 
    super.dismiss(animated: true, completion: nil)
}
// in AppDelegate
// the info plist is only supported portrait also, No need to change it

var currentOrientation : UIInterfaceOrientationMask = .portrait


func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return self.currentOrientation
}

Solution 14 - Ios

According to the documentation of supportedInterfaceOrientations the shouldAutorotate method should return true or YES in Objective-C so that the supportedInterfaceOrientations are considered.

Solution 15 - Ios

My solution is

just added below codes in AppDelegate

enum PossibleOrientations {
  case portrait
    case landscape
        
    func o() -> UIInterfaceOrientationMask {
      switch self {
      case .portrait:
        return .portrait
      case .landscape:
        return .landscapeRight
      }
    }
}
var orientation: UIInterfaceOrientationMask = .portrait

func switchOrientation(to: PossibleOrientations) {
    let keyOrientation = "orientation"
    
    if to == .portrait && UIDevice.current.orientation.isPortrait {
        return
    } else if to == .landscape && UIDevice.current.orientation.isLandscape {
        return
    }
    
    switch to {
    case .portrait:
        orientation = .portrait
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: keyOrientation)
    case .landscape:
        orientation = .landscapeRight
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: keyOrientation)
    }
}

And call below codes to change

override func viewDidLoad() {
    super.viewDidLoad()
    
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        appDelegate.switchOrientation(to: .landscape)
    }
}

or like below

@IBAction func actBack() {
    if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
        appDelegate.switchOrientation(to: .portrait)
    }
    self.navigationController?.popViewController(animated: true)
}

Solution 16 - Ios

// below code put in view controller
// you can change landscapeLeft or portrait

override func viewWillAppear(_ animated: Bool) {
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
    }

override var shouldAutorotate: Bool {
        return true
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .landscapeRight
    }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .landscapeRight
    }

Solution 17 - Ios

I tried many of the answers below but I'm afraid they didn't work. Especially if nav bars and tab bars are also implemented on the app. The solution that worked for me was provided by Sunny Lee on this post here: Sunny Lee, Medium post

Which in turn is an update of this post: Original solution

The only change I made when implementing the post's solution, was to change the part which references .allButUpsideDown to .landscapeleft

Solution 18 - Ios

In Xcode 11 with Swift 5 I Implemented the following. But it only works when the device orientation for the project target does not include all orientations. I disabled the check for Upside Down. After this, the following code works. If all checkboxes are enabled, the code is not called;

class MyController : UINavigationController {

    override var shouldAutorotate: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .landscape
    }

    
}

Solution 19 - Ios

class CustomUIViewController : UIViewController{

    override var   supportedInterfaceOrientations : UIInterfaceOrientationMask{
        
        return  .landscapeLeft
        
    }

}


class ViewController: CustomUIViewController {
.
.
.
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsazzView Question on Stackoverflow
Solution 1 - IossazzView Answer on Stackoverflow
Solution 2 - IosManee iosView Answer on Stackoverflow
Solution 3 - IosZeeshaView Answer on Stackoverflow
Solution 4 - IosAidan MaloneView Answer on Stackoverflow
Solution 5 - IosElangovanView Answer on Stackoverflow
Solution 6 - IosMicrobobView Answer on Stackoverflow
Solution 7 - IosYaroslav HavrylovychView Answer on Stackoverflow
Solution 8 - IosRob SchlackmanView Answer on Stackoverflow
Solution 9 - Iosidris yıldızView Answer on Stackoverflow
Solution 10 - IosGreg TView Answer on Stackoverflow
Solution 11 - IosDaxitoView Answer on Stackoverflow
Solution 12 - IosMayur RathodView Answer on Stackoverflow
Solution 13 - IoskkklcView Answer on Stackoverflow
Solution 14 - IosSebastian WrambaView Answer on Stackoverflow
Solution 15 - IosUtkuView Answer on Stackoverflow
Solution 16 - IosMuthuraj MView Answer on Stackoverflow
Solution 17 - IosZenman CView Answer on Stackoverflow
Solution 18 - IosRaymondView Answer on Stackoverflow
Solution 19 - IosTalView Answer on Stackoverflow