Swift set orientation to specific screen in code - ios

I want to programaticlly set a ViewController orientation and after dismissing the ViewController i want the orientation to return to previous mode
This is what i currently use:
if(GamePrefrences.orientation == "landscape")
{
value = UIInterfaceOrientation.LandscapeLeft.rawValue
}
else
{
value = UIInterfaceOrientation.Portrait.rawValue
}

You can override the preferredInterfaceOrientationForPresentation for
a view controller that is intended to be presented full screen in a
specific orientation.
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/

Related

How to allow only single UIViewController to rotate upside down with Xcode?

In my app all screens have portrait orientation. How to allow only single UIViewController be in two orientation mode: portrait and upside down? When user rotates an iPhone, the UIViewController should rotates too.
First of all, In your application all screens except one screen are in portrait orientation. So You cannot set orientation of your application as portrait. So Set Device Orientation as portrait and LandScape both.
Put following code on which screen you need landscape orientation in ViewDidLoad
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
and
override var shouldAutorotate: Bool {
return true
}
Put following code on all screens where you need only portrait orientation in viewDidLoad
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
If you have any function that continuously called then you can check the device orientation inside this function as-
if UIDevice.current.orientation == UIDeviceOrientation.portraitUpsideDown {
// set your view constraints
}
if UIDevice.current.orientation == UIDeviceOrientation.portrait {
// set your view constraints
}
Otherwise you should create a monitoring function and check device orientation inside the function.

Constraints are not working when changing view orientation

How to force update constraints to position view correctly
My app supports only Portrait orientation, but one view controller should always open in landscape mode. I managed to change orientation forcefully and working properly.
But my view constraints are not working properly when navigating to that view for the very first time or navigate after changing orientation of device. Once it loaded and no orientation change happen its view loaded as expected. Constraints are not framing sub view properly in orientation changed when device is already in that orientation. I am working in xcode 8.2.1.
I checked many links but didn't find any solution for this problem. I tried to update view and constraints also.
self.view.setNeedsUpdateConstraints()
self.view.setNeedsLayout()
self.view.setNeedsDisplay()
self.view.setNeedsFocusUpdate()
Code I am using is -
override func viewDidLoad()
{
super.viewDidLoad()
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
override func viewWillDisappear(_ animated: Bool)
{
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
override var shouldAutorotate: Bool
{
return true
}
View loaded as this when my device is in already in landscape mode and i navigate to this view controller and force for landscape mode. It updated to landscape mode but don't update constraints.
When press back and again select the option it shows as it should be in landscape mode.

Lock only one ViewController's orientation to landscape, and the remaining to portrait

I want to lock orientation in all ViewControllers to portrait, except on one of them, when it is pushed always to be in landscapeRight.
I've tried many solutions, using extensions for UINavigationController, overriding supportedInterfaceOrientations and shouldAutorotate but no luck.
I've found the solution, which for now it is working.
On every view controller you should put this code for supporting only the desired rotation (landscapeRight in the example):
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.landscapeRight
}
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .landscapeRight
}
On the other implement the same methods but with portrait orientation.
Create an extension for UINavigationController
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return (self.topViewController?.supportedInterfaceOrientations)!
}
open override var shouldAutorotate: Bool {
return true
}
Note: Also in Project's target enable all desired supported
orientations.
The only one controller that I wanted to show in landscape mode i presented modally.
You will run into confusion if you use the navigation controller to present views. For example, we usually leverage the navigation controller to handle adding views by calling navigation controller instance functions like performSegueWithIdentifier:sender: or pushViewController:animated:.
Pushing is not going to work like you want it to.
And segues will probably not work either, especially if in IB you created the segue off the navigation controller!
Anyway, if you somehow succeeded in overriding the "parent" navigation controller's orientation, you risk running into edge cases down the line and you are probably using something sketchy that may lose support with any update.
That said, the workaround I have found is:
Step 1
Use presentViewController:animated:completion: to present landscapeRight view controller:
// assuming you're using IB and Main bundle
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let landscapeViewController = storyboard.instantiateViewController(withIdentifier: "LandscapeViewControllerStoryboardID")
self.present(landscapeViewController, animated: true, completion: {
print("landscapeViewController presented")
})
Step 2
Adding the following override to your landscapeViewController actually works now:
/* override the preferredInterfaceOrientationForPresentation for a view controller
that is intended to be presented full screen in a specific orientation.
https://developer.apple.com/reference/uikit/uiviewcontroller */
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .landscapeRight
}
Step 3 (Optional)
With all this working, there is still some weirdness when you dismiss your landscapeViewController: It is in landscape orientation now as well.
This is because the navigation controller is playing nice. In other words, now it's the navigation controller's turn to try to force an orientation change, force it back to the way it was.
In order to do this, you need to store the orientation of the device before you present the landscapeViewController. In the view controller from which you present the landscapeViewController, I stored it in an instance variable:
let ratio = self.view.bounds.width / self.view.bounds.height
if (ratio > 1) { // horizontal/landscape
self.currentOrientationIsPortrait = false // read by navigation controller when dismissing full screen view player
} else {
self.currentOrientationIsPortrait = true // read by navigation controller when dismissing full screen view player
}
Then, I test that view controller instance's value in my navigationController class' preferred orientation override. Let's say the view controller from which I present the landscapeViewController is called presentingViewController:
// execution order: during visibileViewController's dismissal
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
get {
if visibleViewController != nil {
// check if landscapeViewController is being dismissed
if (visibleViewController as? LandscapeViewController) != nil {
// check if the top view controller is one that tracks its orientation (currently only presentingViewController)
if let presentingViewController = self.viewControllers.last as? PresentingViewController {
if presentingViewController.currentOrientationIsPortrait {
return .portrait // TODO should we not support portraitUpsideDown?
}
}
}
// otherwise, return whatever
return visibleViewController!.preferredInterfaceOrientationForPresentation
}
// otherwise, return whatever
return super.preferredInterfaceOrientationForPresentation
}
}
And, bingo. (Hopefully)
EDIT
Also don't forget to put this code in others ViewControllers:
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .portrait
}

Not able to lock orientation for 1 VC

I am trying to get device rotation right.
I am testing on iPad 8.x/9.x simulator
I have 4 VCs
VC1 - Both Portrait and Landscape
VC2 - Both Portrait and Landscape
VC3 - Only Portrait
VC4 - Both Portrait and Landscape
Goal: to have VC3 display PortraitView at all times (same as if app orientation was fixed to portrait).
I tried
#implementation RotationAwareNavigationController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
UIViewController *top = self.topViewController;
return top.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate {
UIViewController *top = self.topViewController;
return [top shouldAutorotate];
}
#end
In VC which is portrait
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
But it does not work meaning view not displayed in Portrait dimensions Am I missing something?
I am sure it can be done as when I use ImagePickerController provided my iOS, it is fixed to Portrait. I just dont know how to do it.
To support differing orientations in different view controllers, you will need to do a few things. First, you need to check all the checkboxes for orientations you want to support in your target's General settings tab.
Second, anytime you call presentViewController or dismissViewController in your app, the UIApplicationDelegate method application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask will be called. You can use this method to restrict (or allow) specific orientations each time a new view controller is presented or dismissed. Unfortunately, it isn't as simple as just returning a UIInterfaceOrientationMask here. You will need to find the view controller that is going to be showing on screen and return the orientations that it supports. Here is an example of this:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
guard let window = window, let rootViewController = window.rootViewController else {
return UIDevice.currentDevice().userInterfaceIdiom == .Pad ? .All : .AllButUpsideDown // iOS defaults
}
// Let the view controller being shown decide what orientation it wants to support. This method will be called anytime a new view controller is presented on screen.
return findVisibleViewController(rootViewController: rootViewController).supportedInterfaceOrientations()
}
/// Searches the view hierarchy recursively and finds the view controller that is currently showing.
private func findVisibleViewController(rootViewController rootViewController: UIViewController) -> UIViewController {
if let presentedViewController = rootViewController.presentedViewController {
// Search for a modal view first.
return self.findVisibleViewController(rootViewController: presentedViewController)
} else if
let navigationController = rootViewController as? UINavigationController,
let visibleViewController = navigationController.visibleViewController {
// Then search navigation controller's views to find its visible controller.
return self.findVisibleViewController(rootViewController: visibleViewController)
} else if let splitViewController = rootViewController as? UISplitViewController {
// Then try the split view controller. This will be the true root view controller. Use the master here since the detail just shows web views.
return self.findVisibleViewController(rootViewController: splitViewController.viewControllers[0])
} else {
// Guess we found the visible view controller, because none of the other conditions were met.
return rootViewController
}
}
findVisibleViewController(_:) is an example from one of my projects and is tailored to the exact view controller hierarchy in my app. You will need to edit this for your own app in a way that makes sense for your hierarchy.
Third, you will need to implement supportedInterfaceOrientations() for most, if not all, of your view controllers.
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return // portrait, landscape, or combinations of those.
}
Finally, this only handles situations where you presented or dismissed something modally. For show (push) segues, the orientation of the navigation controller will be used for every new view controller pushed onto the stack. If you need more fine grained control here, you will need to force the orientation to happen. Here is an example of that:
// Some other view had the screen in landscape, so force the view to return to portrait
UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation")

Force Landscape orientation, Swift

I have a view controller that is designed in portrait view. I have another view controller designed in Landscape view (in storyboards). On my first view controller (portrait VC) i have a button that takes you to the landscape VC. When you click on the button the Landscape view controller shows up in portrait view initially and doesn't change until rotated. I would like to have this landscape view controller automatically show up in landscape without having to rotate it.
Ive been coding this in swift, and have had no luck. In my plist menus i have all orientations enabled.
Any help would be great.
You should add this in the second viewcontroller (designed to be in landscape) in the viewDidLoad
It is in swift3 :
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UIViewController.attemptRotationToDeviceOrientation()
Hope it will help you :)
I believe the best and most robust way to achieve this is by overriding two attributes - and avoid force orientation changes of the windows (as suggested by others).
This is how I got one of my view controllers to always show up in landscape mode, and stay there, without affecting the entire navigation stack. Add both of these overrides to the landscape view controller:
override var shouldAutorotate: Bool {
false
}
This will prevent the view controller from auto-rotating when device orientation changes. Now that the view controller no longer rotates by itself, all you need to do is request the orientation you want:
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
UIInterfaceOrientation.landscapeLeft
}
And voila! Should do what you want.
I'm working on a similar scenario I want to load and keep my first ViewController in Landscape orientation while allowing subviews to switch as needed
I think placing
override func viewDidLoad() {
super.viewDidLoad()
let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
Swift 4
override func viewDidLoad() {
super.viewDidLoad()
let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
should load your view in landscape mode...But if holding phone in portrait orientation it will soon rotate...and that's the part I'm at...How to prevent the rotation.

Resources