如何强制 UIViewController 在 iOS6中面向肖像

由于在 iOS6中不推荐使用 ShouldAutorotateToInterfaceOrientation,我用它来强制使用 只能画肖像的特定视图,那么在 iOS6中使用 ShouldAutorotateToInterfaceOrientation的正确方法是什么呢?这只是我的应用程序的一个区域,所有其他视图可以旋转。

106758 次浏览

I did not test it myself, but the documentation states that you can now override those methods: supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation.

You can probably achieve what you want y setting only the orientation that you want in those methods.

So I ran into the same problem when displaying portrait only modal views. Normally, I'd create a UINavigationController, set the viewController as the rootViewController, then display the UINavigationController as a modal view. But with iOS 6, the viewController will now ask the navigationController for its supported interface orientations (which, by default, is now all for iPad and everything but upside down for iPhone).

Solution: I had to subclass UINavigationController and override the autorotation methods. Kind of lame.

- (BOOL)shouldAutorotate {
return NO;
}


- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
// pre-iOS 6 support
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}

Not to be dull here, but would you be so kind to share your subclass? Thank you.

edit: well, I finally did it, the subclass was dead simple to do. I just had to declare the navigationController in the AppDelegate as UINavigationControllerSubclass instead of the default UINavigationController, then modified your subclass with:

- (BOOL)shouldAutorotate {
return _shouldRotate;
}

so I can set any view I want to rotate or not by calling at viewDidLoad

_navController = (UINavigationController *)self.navigationController;
[_navController setShouldRotate : YES / NO]

Hope this tweak will help others as well, thanks for your tip!

Tip: Make use of

- (NSUInteger)supportedInterfaceOrientations

in your view controllers, so you don't end up by having a portrait desired view in landscape or vice versa.

If you want all of our navigation controllers to respect the top view controller you can use a category so you don't have to go through and change a bunch of class names.

@implementation UINavigationController (Rotation_IOS6)


-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}


-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}


- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}


@end

As a few of the comments point to, this is a quick fix to the problem. A better solution is subclass UINavigationController and put these methods there. A subclass also helps for supporting 6 and 7.

IOS 5

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{


return (interfaceOrientation == UIInterfaceOrientationPortrait);


}

IOS 6

-(BOOL)shouldAutorotate{
return YES;
}


-(NSInteger)supportedInterfaceOrientations{


//    UIInterfaceOrientationMaskLandscape;
//    24
//
//    UIInterfaceOrientationMaskLandscapeLeft;
//    16
//
//    UIInterfaceOrientationMaskLandscapeRight;
//    8
//
//    UIInterfaceOrientationMaskPortrait;
//    2


//    return UIInterfaceOrientationMaskPortrait;
//    or
return 2;
}

The best way for iOS6 specifically is noted in "iOS6 By Tutorials" by the Ray Wenderlich team - http://www.raywenderlich.com/ and is better than subclassing UINavigationController for most cases.

I'm using iOS6 with a storyboard that includes a UINavigationController set as the initial view controller.

//AppDelegate.m - this method is not available pre-iOS6 unfortunately

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;


if(self.window.rootViewController){
UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
orientations = [presentedViewController supportedInterfaceOrientations];
}


return orientations;
}

//MyViewController.m - return whatever orientations you want to support for each UIViewController

- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

1) Check your project settings and info.plist and make sure that only the orientations you want are selected.

2) add the following methods to your topmost view controller(navigation controller/tabbar controller)

- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;


}

3) add the following methods to your app delegate

- (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;


}


- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait;


}

Just go to project.plist then add Supported interface orientation and then add only Portrait (bottom home button) and Portrait (top home button).

You can add or remove there orientation as per your project requirement .

Thanks

I disagree from @aprato answer, because the UIViewController rotation methods are declared in categories themselves, thus resulting in undefined behavior if you override then in another category. Its safer to override them in a UINavigationController (or UITabBarController) subclass

Also, this does not cover the scenario where you push / present / pop from a Landscape view into a portrait only VC or vice-versa. To solve this tough issue (never addressed by Apple), you should:

In iOS <= 4 and iOS >= 6:

UIViewController *vc = [[UIViewController alloc]init];
[self presentModalViewController:vc animated:NO];
[self dismissModalViewControllerAnimated:NO];
[vc release];

In iOS 5:

UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

These will REALLY force UIKit to re-evaluate all your shouldAutorotate , supportedInterfaceOrientations, etc.

I have a relatively complex universal app using UISplitViewController and UISegmentedController, and have a few views that must be presented in Landscape using presentViewController. Using the methods suggested above, I was able to get iPhone ios 5 & 6 to work acceptably, but for some reason the iPad simply refused to present as Landscape. Finally, I found a simple solution (implemented after hours of reading and trial and error) that works for both devices and ios 5 & 6.

Step 1) On the controller, specify the required orientation (more or less as noted above)

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}


-(BOOL)shouldAutorotate
{
return YES;
}


-(NSUInteger)supportedInterfaceOrientations
{
NSInteger mask = UIInterfaceOrientationMaskLandscape;
return mask;


}

Step 2) Create a simple UINavigationController subclass and implement the following methods

-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}

Step 3) Present your viewController

vc = [[MyViewController alloc]init];
MyLandscapeNavigationController *myNavigationController = [[MyLandscapeNavigationController alloc] initWithRootViewController:vc];
[self myNavigationController animated:YES completion:nil];

Hope this is helpful to someone.

This answer relates to the questions asked in the comments of the OP's post:

To force a view to appear in a given oriention put the following in viewWillAppear:

UIApplication* application = [UIApplication sharedApplication];
if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
{
UIViewController *c = [[UIViewController alloc]init];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
}

It's a bit of a hack, but this forces the UIViewController to be presented in portrait even if the previous controller was landscape

UPDATE for iOS7

The methods above are now deprecated, so for iOS 7 use the following:

UIApplication* application = [UIApplication sharedApplication];
if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
{
UIViewController *c = [[UIViewController alloc]init];
[c.view setBackgroundColor:[UIColor redColor]];
[self.navigationController presentViewController:c animated:NO completion:^{
[self.navigationController dismissViewControllerAnimated:YES completion:^{
}];
}];
}

Interestingly, at the time of writing, either the present or dismiss must be animated. If neither are, then you will get a white screen. No idea why this makes it work, but it does! The visual effect is different depending on which is animated.

The answers using subclasses or categories to allow VCs within UINavigationController and UITabBarController classes work well. Launching a portrait-only modal from a landscape tab bar controller failed. If you need to do this, then use the trick of displaying and hiding a non-animated modal view, but do it in the viewDidAppear method. It didn't work for me in viewDidLoad or viewWillAppear.

Apart from that, the solutions above work fine.

Put this in the .m file of each ViewController you don't want to rotate:

- (NSUInteger)supportedInterfaceOrientations
{
//return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
return UIInterfaceOrientationMaskPortrait;
}

See here for more information.

For Monotouch you could do it this way:

public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{
return UIInterfaceOrientationMask.LandscapeRight;
}


public override UIInterfaceOrientation PreferredInterfaceOrientationForPresentation()
{
return UIInterfaceOrientation.LandscapeRight;
}

I have a very good approach mixing https://stackoverflow.com/a/13982508/2516436 and https://stackoverflow.com/a/17578272/2516436

-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;




if(self.window.rootViewController){
UIViewController *presentedViewController = [self topViewControllerWithRootViewController:self.window.rootViewController];
orientations = [presentedViewController supportedInterfaceOrientations];
}


return orientations;
}


- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
if ([rootViewController isKindOfClass:[UITabBarController class]]) {
UITabBarController* tabBarController = (UITabBarController*)rootViewController;
return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
} else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*)rootViewController;
return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
} else if (rootViewController.presentedViewController) {
UIViewController* presentedViewController = rootViewController.presentedViewController;
return [self topViewControllerWithRootViewController:presentedViewController];
} else {
return rootViewController;
}
}

and return whatever orientations you want to support for each UIViewController

- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

I see the many answer but not get the particular idea and answer about the orientation but see the link good understand the orientation and remove the forcefully rotation for ios6.

http://www.disalvotech.com/blog/app-development/iphone/ios-6-rotation-solution/

I think it is help full.