I have a UINavigationController as my UIWindow's rootViewController and a UIViewController (ControllerA) that supports only Portrait orientation is added to the UINavigationController as its rootViewController.
At some point later, I replace UINavigationController's rootViewController with a new UIViewController (ControllerB). ControllerB supports both Portrait and Landscape. I want my initial screen (ControllerA) to only work in Portrait while the rest of the app can supports both Portrait and Landscape.
[self.navigationController setViewControllers:#[viewControllerB] animated:NO];
| UINavigationController launches:
| ----ControllerA (rootViewController): Portrait only
| ----ControllerB (rootViewController): Portrait and Landscape supported
If I start my app in Landscape which is not handled by ControllerA and then move to ControllerB, my content (+status bar) is still in Portrait. If I manually rotate the device at this point, I have the correct content layout.
How can I make ControllerB render itself in the orientation of the device?
Here's what I'm seeing from both ControllerA and ControllerB viewWillAppear method:
navigationController orientation: UIInterfaceOrientationPortrait
UIViewController interfaceOrientation: UIInterfaceOrientationPortrait
<!-----------!!two values are different!!------------------>
[[UIDevice currentDevice] orientation]: UIDeviceOrientationLandscapeLeft
[[UIApplication sharedApplication] statusBarOrientation]: UIInterfaceOrientationPortrait
Phone is physically held in Landscape at this point in time.
Here is the easiest solution (iOS7 only) but it can be expanded to work down to iOS5.
Just write our own UINavigationController (or write a category) and implement supportedInterfaceOrientations.
#implementation MyNavigationController
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
#end
When you want to know the interface orientation always use UIViewController.interfaceOrientation. The statusbar orientation might be wrong (in case of multiple windows) and the device orientation is totally wrong since the device can be Face Up (laying flat on a table) and you cannot infer the interface orientation from that.
Related
I am handling all the orientations in the appdelegate.m file. supportedInterfaceOrientationsForWindow returns UIInterfaceOrientationMaskLandscape for some of my view controllers and UIInterfaceOrientationMaskPortrait for one viewcontroller.
When I move from a landscape viewcontroller to the viewcontroller I want to portrait supportedInterfaceOrientationsForWindow returns UIInterfaceOrientationMaskPortrait but my view controller appears in landscape.
Please help me I've been stuck for a long time. Testing on iPhone 6, iOS 9.
Let's say view A is one of the landscape views of your app and B is the intended portrait view. Now, when the device is initially held in portrait on A and B is then accessed, the view doesn't change the orientation to portrait (since the device was already in portrait).
I'd suggest 'tell' system that B is initially in landscape while preparing the segue and then in your viewDidLoad for B's view controller, change the orientation to portrait.
This way, no matter what the previous view's orientation, B will always open in portrait when it loads.
Use the following code to change the orientation. In case you are using segue use this code in
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
}
The orientation here should be opposite of the desired orientation in the next view controller.
If you have an app with one UIWindow, my understanding is that the rootViewController of that UIWindow will be the UIViewController that receives the rotation/orientation methods like shouldAutoRotate, shouldAutoRotateToInterfaceOrientation, etc.
I'm writing an external library and there is an instance where I create another UIWindow object, set its rootViewController, and make it key and visible. It seems like the rootViewController of the original window is the one that still gets sent the rotation methods and not the new one.
I want to be able to control whether the application can rotate or not while the new window is visible, but it seems like the original window's rootViewController still has control over that. I've tried setting the original window's rootViewController to a rootViewController that prohibits rotation of the screen while my new window is visible and resetting the original window's rootViewController to its original rootViewController but that causes some problems of its own.
Does anyone know how to make a certain UIViewController the one in charge of app rotation?
How are you presenting the new viewController? According to the documentation,the only viewControllers that are asked about supportedInterfaceOrientations are the root view controller, or a view controller that fills the screen. So, on an iPhone, your new viewController should receive the supportedInterfaceOrientations call if it is filling the screen (e.g., presented modally).
shouldAutoRotateToInterfaceOrientation was deprecated as of iOS 6, so you should override supportedInterfaceOrientations instead.
This has worked for me...
In my Case, the destination view appears correct but the status bar and also the UIKeyboard keeps the landscape configuration, making a real mess.
Working around After thousands of recommendations about statusBarOrientation and references read... https://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/_index.html
"The setStatusBarOrientation:animated: method is not deprecated outright. It now works only if the supportedInterfaceOrientations method of the top-most full-screen view controller returns 0. This makes the caller responsible for ensuring that the status bar orientation is consistent."
statusBarOrientation only works if supportedInterfaceOrientations returns 0, so... that give us a guess.
If statusBarOrientation is not as expected, one zero return will do it (if always return 0, the view wont rotate, so:
// if deviceOrientation is A (so I expect statusbarOrientation A
// but statusbarOrientation is B
// return 0
// otherwise
// return user interface orientation for A
- (NSUInteger)supportedInterfaceOrientations {
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
UIInterfaceOrientation statusBarOrientation =[UIApplication sharedApplication].statusBarOrientation;
if(deviceOrientation == UIDeviceOrientationPortrait || deviceOrientation == UIDeviceOrientationPortraitUpsideDown){
if(statusBarOrientation != UIInterfaceOrientationPortrait ||statusBarOrientation != UIInterfaceOrientationPortraitUpsideDown){
return 0;
}
}
// otherwise
return UIInterfaceOrientationMaskPortrait;
}
Now, in viewDidAppear (believe me, I use this call even when the keyboard notification is recived:
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationPortrait;
I have a UINavigationController with a first ViewController that is a UITabBarController, that should not be rotating...
Then pushed UIViewController should rotate...
So far I have subclassed the UINavigationController and implemented those method :
- (BOOL) shouldAutorotate {
return [self.visibleViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations {
return [self.visibleViewController supportedInterfaceOrientations];
}
So it is the child controller that choose if it should autorotate...
I have so far managed to block rotation for UITabBarController and allow rotation for the pushed UIViewController.
The Only thing is, if the UIViewController is in landscape mode, and when I pop it, the UITabBarController will be in Landscape mode too, until the phone is put on the portrait mode, it will come back to normal and not rotate anymore...
I would like that when I pop the Landscape UIViewController, that the UITabBarController is already on portrait mode.
This new iOS 6.0 UI rotation management seems to be a pain !
As you rightly say, the device has not been rotated, so the revealed view controller does not insist on rotating the orientation when the old view controller is popped. If that's the behavior you want, use a presented view controller instead.
You can force interface rotation by rotating the status bar, but only if supportedInterfaceOrientations returns 0.
I am currently developing an application that works with iOS 5 and iOS 6.
Most of my views are only on Portrait orientation except for 1.
RotationNavigationController : Main UINavigationController that overrides supportedInterfaceOrientation and shouldAutorotate.
PageViewController : Pushed in RotationNavigationController and is displayed in Portrait orientation only.
ImageViewController : Pushed after PageViewController. Is displayed with UIInterfaceOrientationMaskAllButUpsideDown.
Here's what I have in the ImageViewController's ViewDidLoad
- (void)viewDidLoad
{
// currentMask is the value returned by supportedInterfaceOrientation
[(RotationNavigationController*)self.navigationController setCurrentMask:UIInterfaceOrientationMaskAllButUpsideDown];
[(RotationNavigationController*)self.navigationController setShouldAutorotate:YES];
}
And when I popViewController from ImageViewController in landscape, I get back to PageViewController in landscape mode too whereas PageViewController only supports Portrait orientation.
Of course, I reset the mask in the ImageViewController's viewWillDisappear to Portrait.
Is there a way for PageViewController to remain in Portrait orientation ?
Thanks for the link emrys57. I found out the solution down there, and it was quite trivial in the end :
In RotationNavigationController, just add the following method :
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
My Application only supports landscape right.
If I start the app holding iPhone in landscape, all successive views are presented in landscape mode. And they do not autorotate to any orientation since my app forbids that using following code in all view controllers
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
However, if I start the App with iPhone held in Portrait Mode, my views are displayed and pushed in Portrait Mode :o (My view controllers do not support Portait Mode at all)
The very first view of my application is Presented as ModalView on a UINavigationController, which is then dismissed and a new HomeController is pushed on to the stack.
How do I re-structure my Application so that the Navigation Controller Pushes Views Correctly?
Here is some code of my didFinishLaunchingWithOptions method
_splashController = [[SplashController alloc] initWithNibName:kViewForSplash bundle:nil];
_navC = [[UINavigationController alloc] init];
_splashController.navC = _navC;
_navC.navigationBarHidden = YES;
[application setStatusBarHidden:YES];
[self.window addSubview:_navC.view];
[_navC presentModalViewController:_splashController animated:YES];
try setting properties Initial interface orientation and Supported interface orientations
to Landscape (right) in Info.plist
this should force the app to launch in Landscape no matter how you hold it