问题
I have a navigation controller which have a few view controllers. I need to support all orientations for all view controllers except one special view controller which only supports landscape. This special view controller appears in the middle of the navigation stack. I have done quite a lot of research but couldn't find any good solution. Here are the links that I have read and tried.
http://www.iphonedevsdk.com/forum/iphone-sdk-development/3219-force-landscape-mode-one-view.html#post60435
How to rotate screen to landscape?
How to autorotate from portrait to landscape mode? iPhone - allow landscape orientation on just one viewcontroller http://goodliffe.blogspot.com/2009/12/iphone-forcing-uiview-to-reorientate.html
Next I am going to try to replace navigation controller with presentModalViewController
in order to display the special view controller. Then I am going to create a new navigation view controller inside the special view controller to push the subsequent view controllers.
If anyone has a better idea, please let me know. Really appreciated!
UPDATE: I have successfully use the method I described above: replace pushViewController
with presentModalViewController
and create a new navigation controller.
回答1:
Every view controller pushed onto the navigation controllers stack have to support the same orientations. This means that it is not possible to have some view controllers only supporting portrait and others only supporting landscape. In other words all view controllers on the same navigation controller stack should return the same in the delegate:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
But there is a simple solution to this! Here is an example for going from portrait to landscape. Here is the steps to do it and below is code to support it.
- Create a ‘fake’ view controller that will be root in a sub navigation controller. This view controller should support landscape.
- Create a new instance of a
UINavigationController
, add an instance of the ‘fake’ view controller as root and an instance of your landscape view controller as second view controller - Present the
UINavigationController
instance as modal from the parent view controller
First, create a new view controller (FakeRootViewController) with this code:
@interface FakeRootViewController : UIViewController
@property (strong, nonatomic) UINavigationController* parentNavigationController;
@end
@implementation FaceRootViewController
@synthesize parentNavigationController;
// viewWillAppear is called when we touch the back button on the navigation bar
(void)viewWillAppear:(BOOL)animated {
// Remove our self from modal view though the parent view controller
[parentNavigationController dismissModalViewControllerAnimated:YES];
}
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
Here is the code to present the view controller that you wish to show in landscape mode:
FakeRootViewController* fakeRootViewController = [[FakeRootViewController alloc] init];[fakeRootViewController.navigationItem setBackBarButtonItem:backButton]; // Set back button
// The parent navigation controller is the one containing the view controllers in portrait mode.
fakeRootViewController.parentNavigationController = parentNavigationController;
UINavigationController* subNavigationController = // Initialize this the same way you have initialized your parent navigation controller.
UIViewController* landscapeViewController = // Initialize the landscape view controller
[subNavigationController setViewControllers:
[NSArray arrayWithObjects:fakeRootViewController,
landscapeViewController, nil] animated:NO];
[_navigationController presentModalViewController:subNavigationController animated:YES];
Remember that the landscapeViewController should also have this implementation:
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationIsLandscape(interfaceOrientation));
}
回答2:
There's a private API to force an orientation change. Put in your pushed view controller's -viewWillAppear:
:
if ([UIDevice instancesRespondToSelector:@selector(setOrientation:)]) {
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];
}
To suppress the compiler warning, add this to the .m file of your view controller:
@interface UIDevice()
- (void)setOrientation:(UIDeviceOrientation)orientation; // private API to let POIEntryVC be pushed over landscape route view
@end
As always, there's a risk of being rejected and a risk of breaking in future OS versions when using private APIs. Do at your own risk!
Generally, presenting a modal view controller is the better solution in most cases.
回答3:
You can make actions: Change Your code with accordance of schellsan suggestion, next - Try to add currentViewController(which will push to navigation viewController) as property to appDelegate. When You attempt to push view controller, set it to current view controller before this. Next - make a subclass of rootViewController in navigation controller. In this subclass owerload method
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return [appDelegate.currentViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
It should works if You not using a navigation bar and pushes new controller after popping an old
回答4:
It should be as simple as implementing
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
in each UIViewController
pushed into your UINavigationController
. In the case that one UIViewController
's view shouldn't rotate, return NO
for that specific orientation in that specific UIViewController
.
There's a gotcha here though, if your UINavigationController
implements
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
it will block its viewControllers from receiving that method. In that case, you should forward the message to the topViewController using
[[self topViewController] shouldAutorotateToInterfaceOrientation:interfaceOrientation];
回答5:
You could try this code in your UINavigationController
to call the current visible view's shouldAutorotateToInterfaceOrientation
. In my case I have the UINavigationController
in a UITabBarController
but you could probably adapt it to other cases.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if ([appDelegate.tabBarController.selectedViewController respondsToSelector:@selector(topViewController)])
{
UINavigationController *nc = (UINavigationController*)appDelegate.tabBarController.selectedViewController;
return [nc.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
来源:https://stackoverflow.com/questions/5023487/support-landscape-for-only-one-view-in-uinavigationcontroller