How to Lock Viewcontroller in Portrait Mode

How to lock orientation of one view controller to portrait mode only in Swift

Things can get quite messy when you have a complicated view hierarchy, like having multiple navigation controllers and/or tab view controllers.

This implementation puts it on the individual view controllers to set when they would like to lock orientations, instead of relying on the App Delegate to find them by iterating through subviews.

Swift 3, 4, 5

In AppDelegate:

/// set orientations you want to be allowed in this property by default
var orientationLock = UIInterfaceOrientationMask.all

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

In some other global struct or helper class, here I created AppUtility:

struct AppUtility {

static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {

if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}

/// OPTIONAL Added method to adjust lock and rotate to the desired orientation
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {

self.lockOrientation(orientation)

UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()
}

}

Then in the desired ViewController you want to lock orientations:

 override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

AppUtility.lockOrientation(.portrait)
// Or to rotate and lock
// AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)

}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

// Don't forget to reset when view is being removed
AppUtility.lockOrientation(.all)
}

If iPad or Universal App

Make sure that "Requires full screen" is checked in Target Settings -> General -> Deployment Info. supportedInterfaceOrientationsFor delegate will not get called if that is not checked.
Sample Image

Lock the orientation of the app in a specific View Controller in Swift

You can force orientation with few steps:

Firstly, In your AppDelegate define a orientation property and conform supportedInterfaceOrientationsFor

var orientationLock = UIInterfaceOrientationMask.portrait

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

Then declare utility struct to set orientation using KVO:

struct AppOrientationUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}

static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}

How to use:

//For portrait
AppOrientationUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)

//For landscape
AppOrientationUtility.lockOrientation(UIInterfaceOrientationMask.landscapeRight, andRotateTo: UIInterfaceOrientation.landscapeRight)

Swift 5, lock first VC to Potrait mode, other VC support all orientations

This is unfortunately a tricky subject. But if you have no UINavigationController or UITabBarController it should be straightforward.

In the AppDelegate, like you've already done, put:

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

In the first UIViewController set:

// This stops the controller from rotating
override var shouldAutorotate: Bool {
false
}

// This will rotate it back to portrait once it's presented again
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
.portrait
}

In the second one you don't need anything because shouldAutorotate defaults to true.

NOTE: If you aren't using a UINavigationController you are probably presenting the second controller. The second controller has to have a modalPresentationStyle of either .fullScreen or .currentContext.

I tried it in a sample project and it's the only way I got it to work.

How to lock orientation for iPhone for certain view controllers - Swift?

You need to follow the below steps to lock rotation for specific ViewControllers :-

Step 1: While creating your project, allow all the orientations. Do not select anything in below image.
Sample Image
Step 2: If you want VC1 to have only Portrait Orientation the implementation then add the below two functions in your ViewController Class

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.all //return the value as per the required orientation
}

override var shouldAutorotate: Bool {
return false
}

Step 3: If you wish VC2 to have all the orientation then do not add any code for it.

So the conclusion:-

In project setting, allow all the orientations for whole project. Restriction should be at ViewControllers level not at project level.

If you wish any VC to have all orientation then don't write any code.

If you wish any VC to have specific orientation then implement two functions above.

How to lock orientation just for one view controller?

This code should work:

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

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

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

If it is now working for you, then I suppose your controller is in some another controller(UINavigationController, UITabBarController, UISplitViewController). In this case you need to use this code in that parent controller.

If your navigation controller contain more than one view controller, and you need to disable orientation only for some of them, then you need to inherit UINavigationController class and write there something like:

class NavigationController: UINavigationController {

var shouldRotate: Bool = true

override func supportedInterfaceOrientations() -> Int {
return shouldRotate ? Int(UIInterfaceOrientationMask.Portrait.rawValue) : Int(UIInterfaceOrientationMask.All.rawValue)
}

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

Then in controller that you need to disable orientation you can disable it for your navigation controller:

class ViewController: UIViewController {

var lastControllerRotationStatus: Bool?

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)

if let navigationController = self.navigationController as? NavigationController {
lastControllerRotationStatus = navigationController.shouldRotate
navigationController.shouldRotate = false
}
}

override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)

if let navigationController = self.navigationController as? NavigationController {
navigationController.shouldRotate = lastControllerRotationStatus ?? true
}
}
}

But remember, that you need to restore old rotation status after your controller will be pushed out navigation controller. In this example I'm saving rotation status before changing it, and restoring it after controller disappeared. Also you can use some other approach. For example you can overload UINavigationController method popViewController, and there set shouldRotate to false.

The same approach with variable setting from controller you can use for controlling rotation from AppDelegate method application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow) -> Int

Then you do not need to inherit from navigation controller.

Your AppDelegate class code will look like:

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

var shouldRotate = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {

return shouldRotate ? Int(UIInterfaceOrientationMask.All.rawValue) : Int(UIInterfaceOrientationMask.Portrait.rawValue)

}

}

And your controller code will look like:

class ViewController: UIViewController {

var lastControllerRotationStatus: Bool?

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)

if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
lastControllerRotationStatus = appDelegate.shouldRotate
appDelegate.shouldRotate = false
}
}

override func viewDidDisappear(animated: Bool) {
super.viewDidDisappear(animated)

if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
appDelegate.shouldRotate = lastControllerRotationStatus ?? true
}
}
}


Related Topics



Leave a reply



Submit