There has been a lot of confusion and a set of corresponding set of questions here on SO how iPhone applications with proper handling for Landscape/Portrait mode autorotation can be implemented. It is especially difficult to implement such an application when starting in landscape mode is desired. The most common observed effect are scrambled layouts and areas of the screen where touches are no longer recognized.
A simple search for questions tagged iphone
and landscape
reveals these issues, which occur under certain scenarios:
Landscape only iPhone app with multiple nibs: App started in Landscape mode, view from first nib is rendered fine, everything view loaded from a different nib is not displayed correctly.
Iphone Landscape mode switching to Portraite mode on loading new controller: Self explanatory
iPhone: In landscape-only, after first addSubview, UITableViewController doesn’t rotate properly: Same issue as above.
iPhone Landscape-Only Utility-Template Application: Layout errors, controller does not seem to recognize the view should be rotated but displays a clipped portrait view in landscape mode, causing half of the screen to stay blank.
presentModalViewController in landscape after portrait viewController: Modal views are not correctly rendered either.
A set of different solutions have been presented, some of them including completely custom animation via CoreGraphics, while others build on the observation that the first view controller loaded from the main nib is always displayed correct.
I have spent a significant amount of time investigating this issue and finally found a solution that is not only a partial solution but should work under all these circumstances. It is my intend with this CW post to provide sort of a FAQ for others having issues with UIViewControllers in Landscape mode.
Please provide feedback and help improve the quality of this Post by incorporating any related observations. Feel free to edit and post other/better answers if you know of any.
In your view controller, override shouldAutorotateToInterfaceOrientation: to declare your supported interface orientations. This property will/should be checked by the controller infrastructure everytime the device orientation changes.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
return (orientation == UIInterfaceOrientationLandscapeRight);
}
This is the absolute minimum your view controller needs to do. If you want to launch your application in landscape mode, you need to add the following key to your .plist
file:
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
Apple recommends starting landscape only applications in Landscape Right mode (see the HIG under User Experience Guidelines > Start Instantly).
Everytime you try to load a different view controller other than that loaded from the main nib, your view controller is neither interrogated about it's supported interface orientations nor is its frame set correctly. Only the first view controller bound to the window will be layed out correctly.
Other people have suggested using a "MasterViewController" hooked up to the main window to which other controllers add their views as subviews instead of hooking directly into the window. While I have found this solutions is a viable option, it does not work correctly in the case of modal view controllers added to those said subviews. There's also a problem if you have some subviews that should be able to autorotate (what the master controller will prevent).
The usage of undocumented API's to force a certain interface orientation is not an option either.
The best solution I have found so far is a modification of the "MasterViewController" workaround. Instead of using a custom "MasterViewController", a UINavigationController
with hidden Navigation Bar and hidden Tab Bar is used. If all other views are pushed/popped from the navigation stack of this controller, auto-rotations of controllers on that stack will be managed correctly.
Modal controllers presented via presentModalViewController:animated:
from any of the view controllers on the UINavigationController
's navigation stack will be rotated and rendered with correct layout. If you want your modal view controller to be rotatable to a different orientation than that of the parent view controller, you need to return the desired orientation from the shouldAutorotateToInterfaceOrientation
method of the parent controller while the modal view is presented. In order to properly restore the interface orientation when the modal controller is dismissed, you need to make sure shouldAutorotateToInterfaceOrientation
returns the desired orientation for the parent controller before you call dismissModalViewController:animated:
. You can use a private BOOL
on your view controller to manage that (e.g. BOOL isModalMailControllerActive_
).
I'll add a piece of sample code soon, It's just to late now. Please let me know if any unresolved issues remain or anything is unclear about this post. Feel free to edit and improve.