iOS Swift 5 Change device orientation manually - ios

In iOS swift 5 project I need to support multiple device orientations in only one scene.
I have the following requirements:
If device is rotated, device orientation should not be changed.
If button is tapped, device orientation should be changed (left
rotation)
This means that in only one UIViewController user should be able to change device orientation manually by tapping on a button, but rotating the device should not do anything.

One simple way to accomplish this is by setting your supported orientation on the AppDelegate (_:supportedInterfaceOrientationsFor:)
Create a local variable there
var orientation: UIInterfaceOrientationMask = .portrait
and then return that variable as supported orientation
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return orientation
}
on the ViewController that you want to rotate with a touch of a button, you can change the supported orientation on the app delegate and than force the device orientation to change so that the view is rotated
private func changeSupportedOrientation() {
let delegate = UIApplication.shared.delegate as! AppDelegate
switch delegate.orientation {
case .portrait:
delegate.orientation = .landscapeLeft
default:
delegate.orientation = .portrait
}
}
#IBAction func rotateButtonTapped(_ sender: UIButton) {
changeSupportedOrientation()
switch UIDevice.current.orientation {
case .landscapeLeft:
UIDevice.current.setValue(UIDeviceOrientation.portrait.rawValue, forKey: "orientation")
default:
UIDevice.current.setValue(UIDeviceOrientation.landscapeLeft.rawValue, forKey: "orientation")
}
}
this will force the orientation of the device to change and then rotate your view. If you tap on the button again the orientation will be back to .portrait
Please be careful of using this as you need to really consider your navigation stack to make sure that only the top of the navigation stack support rotation and can only be pop from the navigation stack after the orientation is set back to the original which is .portrait only.

Related

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

I'm working on an app that requires locking the orientation in each view controller.
I read this article, but it did not work for me.
In the project settings, I set the device orientation to Portrait, Landscape Left, and Landscape right.
I have two view controllers, VC1 and VC2, and I want to lock the VC1's orientation vertically only. I want to lock the VC2's orientation, on the other hand, horizontally only.
Then I added the following code in VC1.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait //return the value as per the required orientation
}
override var shouldAutorotate: Bool {
return false
}
But, I can still able to rotate the VC1 horizontally...
Side note: VC1 contains a navigation bar and a tab bar.
If you know how to solve this issue, please let me know...
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)

Can't force rotate of orientation in landscape mode

My entire app is portrait mode. However, one screen needs to be able to rotate to landscape (for playing video). I am able to enable all orientation rotations with no problem via #Jonathan Danek's answer.
In my AppDelegate I have:
static var orientationLock = UIInterfaceOrientationMask.portrait
and subsequently:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return AppDelegate.orientationLock
}
So to enable rotation for all orientations, I do the following as the video view-controller is presented:
AppDelegate.orientationLock = UIInterfaceOrientationMask.all
UINavigationController.attemptRotationToDeviceOrientation()
This works great 👍 ..the problem is when I try to change it back to only portrait mode.
I am trying this as the video view-controller is dismissed:
AppDelegate.orientationLock = UIInterfaceOrientationMask.portrait
UINavigationController.attemptRotationToDeviceOrientation()
..but it does not trigger the above AppDelegate's supportedInterfaceOrientationsForWindow function if the video view-controller is currently rotated to Landscape. What's interesting is that it will trigger the function if the video view-controller is currently rotated to Portrait.
So I'm wondering how I can set my one view-controller to have all orientations, but then change it back to just portrait?
Have you tried to overwrite supportedInterfaceOrientations in your controller? That might help.
Example:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.all
}
Returns all of the interface orientations that the view controller supports.
Apple Doc - supportedInterfaceOrientations
Swift 5.1 short version:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
.all
}

How to restore right orientation of the UIViewController after orientation was forced to landscape by another (iOS)?

The problem I am having is a bit strange and I can't seem to find any good solutions out there. So I have two UIViewControllers, one is allowed to take all orientations, another can only be viewed in landscape
internal override func shouldAutorotate() -> Bool {
return true
}
internal override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Lanscape
}
If I am in portrait mode on ViewController1, and I push VC2, it rotates just fine, but when I leave VC2, VC1 is stuck in landscape even though device itself is in portrait mode. How can I fix it? I tried calling AttemptToRotateToDeviceOrientation method in ViewDidAppear method of VC1, but it doesn't do anything
You just need to create app delegate instance in your view controller like:
let appDel = UIApplication.shared.delegate as! AppDelegate
On Viewwillappear()of view controller just add this code for landscape
appDel.myOrientation = .landscape
UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
And for portrait add this code
appDel.myOrientation = .portrait
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")

Portrait mode all views except one view to be rotated programatically to landscape under a UINavigationController

First of all, my project hierarchy:
tab bar controller -> navigation master -> view controllers
I have one page I want to rotate in landscape mode.
My project is compatible with all the device orientation modes.
I've manually frozen all the view controllers with autorotation returning false:
override func shouldAutorotate() -> Bool {
return false
}
except the one I want to rotate. S
I'm trying this in the view controller I want to be displayed in landscape:
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
But does nothing.
Also, I experimented an issue with this method. When the app was starting in landscape, the view was displaying in landscape and was unable to rotate. I manage to fix it with this method In my app delegate:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if(self.restrictRotation == false){
return UIInterfaceOrientationMask.Portrait//UIInterfaceOrientationMaskPortrait
}
else{
return UIInterfaceOrientationMask.All//UIInterfaceOrientationMaskAll
}
}
And in viewDidLoad setting the var to true where I want to allow rotations:
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.restrictRotation = true
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
I've tested with the debugger and the method application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) is called, but the app don't rotate. I guess is because with a tab bar and a navigation controller, things has to be done differently, but don't know how exactly. Can anyone help with this?

Need some controllers in landscape

I have complex application with lot of ViewControllers. The whole app is designed to be in portrait mode and its locked to it.
Now I need part of app to be able to show content in landscape. Everything is working fine, excluding one scenario:
I'm on controller, which is locked to portrait and has device in landscape position - I navigate to new controller, which is landscape enabled. Then the new controller doesn't rotate to landscape. If I rotate phone to portrait and landscape again, the controller rotates correctly (if I enter the new controller with device in portrait, everything is fine and window rotates, if I rotate device.
I tried:
let value = UIDevice.currentDevice().orientation.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
in viewWillAppear, but it didn't work (I suppose it can't work, because UIDevice.currentDevice().orientation is in requested orientation already.
Try adding this to your code and see if you get the results you are expecting.
override func viewDidLoad() {
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
shouldAutorotate()
}
override func shouldAutorotate() -> Bool {
switch UIDevice.currentDevice().orientation {
case .Portrait, .PortraitUpsideDown, .Unknown:
return true
default:
return false
}
}

Resources