I'm embedding my app in a UINavigationController, I want most of myViewControllers except one to be Portrait, I've read a lot of questions but could not find a correct answer that works for me.
In my target I'm selecting Device Orientation : Portrait, Landscape Right
I'm adding this to my first ViewController:
-(BOOL)shouldAutorotate{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations{
return (UIInterfaceOrientationPortrait);
}
But when I rotate the device left the ViewController rotates as well.
Why is it rotating?
You can't easily do in iOS 7 what you're describing. A UINavigationController does not consult its children as to what rotations they like; whatever the permitted rotations of the UINavigationController, those are the permitted rotations of the app, regardless of which child happens to be showing at that moment.
The only really legal and built-in way to force rotation is to use a presented ("modal") view controller that takes over the screen. Its rotation settings are consulted because it is now in charge of the screen.
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")
Is it possible to only rotate one of the child vc?
The xamarin code (it's pretty much the same as swift)
mainVC.addChildViewController(childVC_A);
mainVC.addChildViewController(childVC_B);
mainVC.addSubView(childVC_A.View);
mainVC.addSubView(childVC_B.View);
the mainVC has shouldAutorotate to be true and GetSupportedInterfaceOrientations to return all orientation;
I only want childVC_A to autorotate to landscape mode and childVC_B to always remain in portrait mode.
childVC_A has shouldAutorotate to be true and GetSupportedInterfaceOrientations to return all orientations;
childVC_B has shouldAutorotate to be false and GetSupportedInterfaceOrientations to return only portrait;
Is this possible?
I can't speak to the specifics of Xamarin, but normally it is not possible to use the system rotation mechanism to rotate part of the screen and not another part (it is also difficult to do something like rotate only one of the tabs of a UITabController effectively). You're more or less stuck hooking into the accelerometer (CoreLocation) and implementing that partial rotation on your own.
I tried to find a solution but so much information which doesn't work. My last try was using the following:
UIApplication.sharedApplication().setStatusBarOrientation(UIInterfaceOrientation.LandscapeRight, animated: false)
This however, was deprecated from iOS 9 and couldn't find any way to force rotate with UINavigationController. My app mainly uses Portrait Orientation and only one view needs to be Landscape. I need to force Landscape on one View and rest to keep as Portrait. Any help would be highly appreciated!
Some of the questions I checked are:
Setting device orientation in Swift iOS
How do I programmatically set device orientation in iOS7?
Why can't I force landscape orientation when use UINavigationController?
If this is something you really want to do, subclass UINavigationController then add this code:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Landscape
}
Trying to force an orientation imperatively is unwise; it's better to tell iOS what you want (as above) then let it calculate the orientation as best it can.
We had to do this same thing in our app as well. Initially we worked with a hack. But eventually we switched the "Landscape" VC to a modal rather than part of navigation view controller stack. I would suggest you do that. But if you really want to, here is how you do it.
Subclass Navigation VC.
in supportedInterfaceOrientaions check for VC type & return appropriate orientation (landscape for one you want, portrait for rest)
This itself wont autorotate that VC to landscape, so here is the hack.
In viewDidLoad/viewDidAppear of "landscape" VC, push another generic VC object & pop it subsequently
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:c animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];
This used to work in iOS7 & we switched to modal after that. so this might now work in later versions.
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 have a screen that supports Device Orientation.
Everything is working fine except for the fact that when I rotate the device upside down (home button at top), the rotation doesn't work (it's stuck on the last landscape settings).
I know of several places needed be updated to support this:
In the VC itself, I added the methods:
In the Project Target, I updated as follow:
In the Storyboard VC Scene, I updated as follow:
What am I missing here?
You also have to allow rotating to all orientations in every parent view controller of the current main view controller. For example, if your view controller is in navigation controller, try subclassing it and override the same methods as in your example.
Edit: As #JordanC mentioned, since iOS 7 you can implement UINavigationControllerDelegate method to return custom supported orientations:
- (UIInterfaceOrientationMask)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController
As #eGanges mentioned the key point could be to subclass your UITabBarController (and override supportedInterfaceOrientations) if that is your initial view controller, in that case this is the only controller you should subclass (and of course you should add all the supported interface orientations to your app Info.plist file UISupportedInterfaceOrientations key)
Have you tested on real device?
anyway try this:
- (NSUInteger)supportedInterfaceOrientations {
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
}