I'm a little confused about the auto rotation methods in iOS. I'm using Swift for my app, which contains a tab bar controller and a nav bar controller.
The issue is I want all view controllers to be locked in Portrait mode except for one view controller which shows an image. I want this view controller to be able to be seen in both Portrait or in Landscape orientation based on how the user wants to view the image.
If I turn off the left/right rotation in the deployment info settings and call the shouldAutorotate() - return true method then the view controller with the image stays locked and won't rotate.
If I turn on the left/right rotation in deployment info settings and call the shouldAutorotate() - return false in the view controllers that I want locked then they still auto rotate.
-I feel like this shouldn't be as difficult as it is and can't find a solid answer on this. I'm a little newer to app development so any advice suggestions are appreciated.
Thanks in advance!
Have you set the supportedInterfaceOrientations?
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.AllButUpsideDown
}
Related
I have a app that is locked to portrait in all views except one that is AllButUpsideDown. The approach i am using is to enable Portrait, Landscape Left and Landscape Right in the targets general settings menu. Then have subclasses of UINavigationController and UITabBarController that override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask and returns .Portrait.
Then in my view controller that needs to be able to be rotated I have also overridden func supportedInterfaceOrientations() -> UIInterfaceOrientationMask and returns .AllButUpsideDown. This works fine since this view controller is only presented as a modal i.e aViewController.presentViewController().
All of this work as expected on iOS9 on iOS8 however if i close the rotatable view controller while in landscape the UI will be scaled to landscape altho it will be displayed in portrait.
Anyone know what to do about this? Am I approaching this rotation thing wrong from the start? Any clean fixes? Workarounds? Hacks?
UPDATE
My problem originated from me using a custom transition to present and dismiss the view controller that could rotate. I tried to work around it for some time with bunch of different solutions. The closest I got to a solution was to use a separate UIWindow for my rotatable view controller, that worked except a issue with the carrier bar still being in the wrong orientation, and that was something I did not manage to solve.
The solution(not really a solution) I went with was to only use the custom transition in iOS9+ and on iOS8 use the default present transition.
I had the similar issue when navigation back from VC, that supports landscape to the one that is only portrait. I didn't find a clean workaround. These couple of lines are not recommended to use, but if you are desperate you can force your device orientation when you are about to dismiss.
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
I have an app that supports all orientation, but in certain cases I would like to temporarily restrict it to only portrait. It's not a mix of view controllers that need to be only portrait and those that can be any orientation, rather, I would like to disable orientation changes when the user clicks a button on screen, while the view controller stays the same.
When looking for ways to restrict UI orientation of a UIViewController, methods that pop up are supportedInterfaceOrientations() and preferredInterfaceOrientationForPresentation(). Both don't quite work for me:
supportedInterfaceOrientations() runs only once when the view is loaded, so even if I make its return value conditional, it does not run after the condition changes.
my view controllers are presented inside a navigation stack, not modally, so preferredInterfaceOrientationForPresentation() does not run at all.
Is there any way to achieve the effect I'm looking for?
I'm going to assume your view controllers are in a navigation controller. Without the navigation controller, supportedInterfaceOrientations() is called whenever the device is rotated. HOWEVER, when embedded in a navigation controller, the navigation controller decides whether the view controller should rotate. (See examples below)
What do you need to do? Implement supportedInterfaceOrientations() in a custom UINavigationController like so:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if self.topViewController is RootViewController {
return .Portrait
} else {
return .All
}
}
This is a duplicate question of this but I didn't see a swift example. For whatever class you want to stay in portrait, just replace RootViewController with the class name.
I want to say in each view controller which orientation he supports and restrict it to them. This is what I tried:
Use a custom navigation controller like in one of my old posts, but that doesn't seem to work anymore. The solution is similar to iOS 6 - (BOOL)shouldAutorotate not getting called for navigation controllers pushed viewControllers
I also tried Setting Orientation in Xamarin iOS, but the app crashes on the RootViewController. I get a NullReferenceException here.
Furthermore I tried IOS 8 : Restricting orientation on a View Controller from the deleted blog from Shri Chakraborty (shrixamarin). Here the app also crashes if I use application:supportedInterfaceOrientationsForWindow:, because the RootViewController is null. The solution seems to be similar to How do I restrict orientation per view controller in iOS7 in a navigation controller hierarchy
None of the solutions seems to work. Or do I miss something important? How can I restrict the orientation that view controller A is landscape only, view controller B is portrait only and view controller C can be shown in all available orientations?
Overriding GetSupportedInterfaceOrientations() in your view controller should solve the problem:
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations () {
return UIInterfaceOrientationMask.LandscapeRight;
}
The orientations you return here will be intersected with the app's allowed orientations (project properties) or with what you allow in your app delegate. So be sure to specify all orientations there or the maximum set of orientations you want to support.
See here for documentation.
In my iPhone app I have a view that I want to show only in portrait mode. When navigating to that view it should be automatically displayed in portrait view. When navigating away, the orientation should change back to what it was, or, if the device orientation has changed, adapt to that. I could find information on forcing an orientation and preventing auto-rotate. I could not find anything on how to change back to the correct orientation after navigating away from that view.
So my idea was to
save the initial orientation (store in currentOrientation)
subscribe to orientation change event to keep track of orientation changes while the content is locked to portrait (update currentOrientation)
when leaving the view, restore the correct orientation using the currentOrientation value.
Edit (code now removed): Apart from it not working it was a dangerous way to go as it made extensive use of unsupported APIs.
Edit:
I believe this question can now be boiled down to the following:
Is there a documented, supported way to force the interface orientation independent of the device orientation? setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") has been recommended many times on SO and elsewhere but it does indeed seem to be an unsupported hack.
Is there a documented, supported way to update the interface orientation to the device orientation? That would be needed to "recover" from the forced interface orientation in another view without having to trigger auto rotation by turning the device back and forth.
Supported are supportedInterfaceOrientations() and shouldAutorotate(). But these will only lock the interfaceOrientation after the device has been turned to that position. They do not prevent wrong initial orientation.
There are many questions similar to this one, showing that this problem setting is not uncommon, but so far no satisfactory and complete solution using supported methods.
I had a similar problem except I needed one view controller to only work in Landscape mode and another when it was in portrait. The way I achieved this was making a custom 'root' view controller. Then on the viewWillTransitionToSize method for that controller checking for orientation and non animatedly pushing the correct view controller (so it looks like a rotation to the user). And then in Interface Builder I set the view controller's orientation property explicitly instead of being inferred. You could apply this solution by having only the landscape orientation set on the restricted view controller and then on the portrait rotation doing nothing and disabling auto rotation on the restricted view controller.
Update
I haven't had the time to test any of these but these are just the ideas I used when implementing my solution for a different VC for a different orientation, some combination of the following should hopefully work I can't be a 100% certain about it cause I did this some months ago and don't exactly remember what did and didn't work.
First of all make sure that you have setup the constraints as shown in the screenshot. Mine has iPad full screen and landscape because that's what I was doing change yours to whatever you need (portrait and the size can be inferred).
Now before doing anything else I would first check to see if this solved the problem. I needed the root view controller cause I needed a different VC for portrait and and a different one for landscape. You only need to restrict it so if this works than that's perfect otherwise there are a few other things you can try as mentioned below.
Once that's setup I would first go to the view controller who you want to restrict's class and prevent autorotation using:
override func shouldAutorotate() -> Bool {
return false
}
Now if you do that since you are restricting to portrait I'm guessing you don't really care about upside down so you don't need to do anything additional. If you do want to use the viewWillTransitionToSize method and rotate manually.
If things still don't work you can finally try the root controller way (but I would use this in the last case). Heres a sketch of it:
class VC : UIViewController {
override func viewDidLoad () {
UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: "UIDeviceOrientationDidChangeNotification", object: nil)
// this gives you access to notifications about rotations
}
func orientationChanged(sender: NSNotification)
{
// Here check the orientation using this:
if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) { // Landscape }
if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) { // Portrait }
// Now once only allow the portrait one to go in that conditional part of the view. If you're using a navigation controller push the vc otherwise just use presentViewController:animated:
}
}
I used the different paths for the if statements to push the one I wanted accordingly but you can do just push the portrait one manually for both and hopefully one of the ways above will help you.
I want the general orientation of my app to be portrait, but for one specific UIViewController (VC2B) I want the user to be able to tilt the device for landscape orientation. (VC2B has a navigation bar)
I have tried different types of code but without luck. Ideally I would like to to enable rotation in only UIViewController (VC2B). But this seems impossible, instead it seems you have to enable all the orientations in the General section and then turn them off programatically - is this really true?
Anyway I have tried various things
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientation.LandscapeLeft.rawValue) | Int(UIInterfaceOrientation.LandscapeRight.rawValue) | Int(UIInterfaceOrientation.Portrait.rawValue)
}
The below answer suggest overriding shouldAutorotate to false, but that does not work if the page is loaded in landscape mode, then it is just stuck on landscape.
Allow all orientations for only one view controller
Question: How can I in a tidy way, without adding code to all my UIViewControllers, allow multiple orientations for only one UIViewController?
Do the following as I did this when I faced same situation:
enable the desired orientations in the General Info Screen
in each ViewController.swift, override the autorotation function with either true or false (true for the one that should rotate, false for the others)