My app has about 10 different UIViewControllers, just one of which I want to switch to landscape mode if the device is rotated. (All the rest, I want to keep in portrait.)
I have seen examples using the way you are doing it but couldn't get it to work properly. I found the better way to do it from Apples examples. Basically you implement a presentModalViewController and create another view. UIKit does a basic rotation animation and fades between the view. You have to implement the rotated view as a delegate class so it can call back to its calling class to dismiss it and update the orientation.
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
NSLog(@"orientationChanged");
[self performSelector:@selector(updateLandscapeView) withObject:nil afterDelay:0];
}
And then to display a landscape screen:
- (void)updateLandscapeView
{
PortraitView *portraitView = [[PortraitView alloc] init];
portraitView.delegate = self;
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController: portraitView animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
[portraitView release];
}
when you implement UINavigationController, this class is your the parent that controls all the children viewControllers that will be pushed into the stack. Therefore, the RootViewController is the only controller that say Yes or No to autorotation. Even if, you are passing Yes to auto-rotation in children view controllers, they don't count!
This is the nature of UINavigationController. So to change it, you have two choices:
1- Manually manipulate it, which requires you to go through some cumbersome codes.
2- Change your design so that it is more compatible to UINavigationController. That single view that should rotate, should be get called by the RootViewController (not the Navigation Root View Controller -- they are named the same, but are totally different), the view that hosts NavController. And when the device rotates, it will either push the NavController to the view or the other.
3- The other method, which will also work, but is not recommended because it violates the concept of MVC, is that your NavController can listen to Notifications. That particular child view that CAN and SHOULD rotate can cast a notification -- e.g. rotateMe, and once the NavController hears it, it rotates.
As I said, it will work, but it violates the MVC model -- which is fine to Apple, but from programming perspective is not recommended.
If you need further explanation about either of them, please let me know.
Returns YES on the navigation controller subclass only if [[navController topViewController] isKindOfClass:[RotatingViewController class]]
I do this by having a root view controller (this could be a UITabBarController) and in it's viewDidLoad method i subscribe to rotation events:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didRotate:)
name:@"UIDeviceOrientationDidChangeNotification"
object:nil];
Then in the didRotate: method i look at which view controller is visible when the rotation happened, and what orientation the phone is in:
- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
/*
DEVICE JUST ROTATED TO PORTRAIT MODE
orientation == UIDeviceOrientationFaceUp ||
orientation == UIDeviceOrientationFaceDown
*/
if(orientation == UIDeviceOrientationPortrait) {
/*
DEVICE JUST ROTATED TO LANDSCAPE MODE
*/
}else if(orientation == UIInterfaceOrientationLandscapeLeft ||
orientation == UIInterfaceOrientationLandscapeRight) {
}
}
Within that didRotate: you can look at which is the visible viewController and do what you want from there.
I present a modal view controller in landscape mode when a particular view controller is visible and the phone is rotated into landscape. If any other view controller is visible, i ignore the event.
I force my modal view controller to display in landscape mode in its viewWillAppear method - i can give anyone this code if they want it.
Hope this helps.
Dave