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.
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.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
Nstextattachment Image Not Shown in Nstextview (But in Uitextview)
Implementing Codable for UIcolor
How to Add Action to Uialertview in Swift (iOS 7)
How to Record and Save at 240 Frames Per Second
iOS Swift Streaming App Does Not Play Music in Background Mode
How to Fix Crash When Tap to Select Row After Scrolling The Tableview
Populating Input Text Field in Wkwebview
My App Is Getting Crashed on UIdocumentpickerviewcontroller
How to Convert Cgpoint in Nsvalue in Swift
How to Animate Uilabel's Textcolor Change
iOS 11 CPU Throttling and Idle Timer
Terminating App Due to Uncaught Exception 'Nsinvalidargumentexception' - iOS Google Sign In
Uialertview's Textfield Does Not Show Keyboard in iOS8
Testflight Sdk and iOS Simulator - How to Use
Mpvolumeview Does Not Show Route Button on Launch
Swift 3 Weird Crashes (Type Inference)
iOS 13.2 Message: Nehelper Sent Invalid Result Code [1] for Wi-Fi Information Request