如何在iOS 6中强制将UIViewController设置为竖屏方向

86

由于在iOS 6中ShouldAutorotateToInterfaceOrientation已被弃用,而我之前使用它来强制一个特定的视图仅支持竖屏,那么在iOS 6中正确的做法是什么?这仅适用于我的应用程序中的一个区域,所有其他视图都可以旋转。

16个回答

117

如果你希望所有的导航控制器都遵循顶部视图控制器,你可以使用一个分类(category),这样你就不必逐个更改类名。

@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

正如一些评论所指出的那样,这只是一个快速解决问题的方法。更好的解决方案是创建UINavigationController的子类,然后在其中放置这些方法。子类还有助于支持6和7。


3
我可以确认这个方法是可行的。如果你愿意,你也可以把 "[self.viewControllers lastObject]" 替换成 "self.topViewController"。 - Wayne Liu
3
如果您正在使用UITabBarController,那么这个UINavigationController类别没有用处。相反,您应该在UITabBarController上创建类别... - Borut Tomazin
46
这个解决方案的问题在于,当你弹出一个横屏控制器并且你的新顶部控制器只支持竖屏时(或者反之亦然),这些回调函数就不会被调用,我还没有找到方法来强制新的顶部控制器进入正确的方向。有什么想法吗? - Lope
4
我创建了一个UINavigationController的子类,并将其设置为窗口的根控制器。我实现了所有3个方法,在更改设备旋转时运行良好,但问题是,当您弹出视图控制器时,这些方法(与旋转无关的任何内容)都没有被调用。 - Lope
4
如果我从横屏视图控制器执行推入或弹出操作,则会强制转换UIViewController为横向模式。然而,如果我旋转到纵向模式,则一切都正常,它不会再次变为横向模式。唯一的问题是在从横向模式进行推入或弹出时出现。请帮忙解决。 - Tariq
显示剩余13条评论

63

"iOS6 By Tutorials"由Ray Wenderlich团队编写,指出了特别适合iOS6的最佳方法 - http://www.raywenderlich.com/,在大多数情况下比子类化UINavigationController更好。

我正在使用带有UINavigationController的故事板作为初始视图控制器。

//AppDelegate.m - 不幸的是,在iOS6之前,此方法不可用。

- (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 - 返回您想为每个UIViewController支持的方向

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

1
这是iOS 6的最佳解决方案。 - Homam
2
@Phil,它对于iOS 6正常工作。但在某些情况下,例如从横向模式切换到纵向模式,它就无法工作了。你知道为什么会这样吗? - Kirti Nikam
1
非常有用的解决方案。对我来说是最好的。 - oscar castellon
1
正在处理iOS 7 beta 6。在我的情况下,从横屏切换到竖屏时可以正常工作。 - Martin Berger
我在iOS 6上,但这不起作用。它在“UIViewController *presentedViewController”行中断。 - Marcel Marino
显示剩余4条评论

39
这个答案与OP帖子评论中提出的问题相关:
要强制一个视图以给定的方向显示,请在viewWillAppear中添加以下内容:
UIApplication* application = [UIApplication sharedApplication];
if (application.statusBarOrientation != UIInterfaceOrientationPortrait)
{
    UIViewController *c = [[UIViewController alloc]init];
    [self presentModalViewController:c animated:NO];
    [self dismissModalViewControllerAnimated:NO];
}

这是一种有点取巧的方法,但它可以强制UIViewController在先前的控制器为横屏的情况下以竖屏方式呈现。
iOS7更新:
上述方法已经过时,因此在iOS 7中请使用以下方法:
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:^{
            }];
     }];
}

有趣的是,在编写时,当前关闭必须进行动画处理。如果两者都没有动画效果,则会出现白屏。不知道为什么这样做可以使其正常工作,但确实如此!视觉效果取决于哪一个被动画化。


谢谢,我会在找到一些空闲时间后尝试一下。 - Lope
2
有人想出如何在正确旋转动画的同时使其正常工作了吗?在没有动画的情况下从纵向切换到横向有点令人不适。虽然它能够工作,但希望能让它更好一些。 - Dennis Munsie
@Craig Watkinson,Storyboard中无法工作。presentModalViewController和dismissModalViewControllerAnimated已经被弃用,如果我使用presentVirecontroller,则无法工作。请帮帮我。 - Kalpesh
1
对于iOS8,dismissViewControllerAnimated会导致崩溃。我发现调用[NWSAppDelegate sharedInstance].window.rootViewController = nil; [NWSAppDelegate sharedInstance].window.rootViewController = previousRootController可以很好地解决问题。 - Alexey
@Alexey 对我来说,在iOS8上它没有崩溃。 - AlexanderN
显示剩余4条评论

36

我在显示竖屏模态视图时遇到了同样的问题。通常,我会创建一个UINavigationController,将viewController设置为rootViewController,然后将UINavigationController作为模态视图显示。但是在iOS 6中,viewController现在会询问navigationController支持的界面方向(默认情况下,iPad支持所有方向,iPhone除了倒置之外都支持)。

解决方案:我不得不子类化UINavigationController并重写自动旋转方法。有点困难。

- (BOOL)shouldAutorotate {
    return NO;
}

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

1
我的电脑上无法运行。我有这段代码,全局设置为所有方向。 - phil88530
2
supportedInterfaceOrientations 应该返回一个 NSUInteger,而不是 BOOL。请参阅 http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/RespondingtoDeviceOrientationChanges/RespondingtoDeviceOrientationChanges.html。 - tomwhipple
它起作用了,对我来说是tabbarcontroller,再次感谢你的回答。 - otakuProgrammer
就我个人而言,我也不得不走这条路... 没有其他办法可行。谢谢。 - Steve N

16

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;
}

太好了!需要尽可能置于此线程的顶部。因为这是解决问题最简单的方法。 - Olex
@Roshan Jalgaonkar 在iOS 6中,它对我不起作用,我只需要带有向下主页按钮的纵向模式,我该如何设置UIOrientation... - Karthik
@Karthik,要使它起作用,您需要在应用程序的目标中启用支持的旋转。 - Alejandro Iván

10

我不同意@aprato的回答,因为UIViewController旋转方法是在它们自己的类别中声明的,因此如果您在另一个类别中重写它们,会导致未定义的行为。更安全的做法是在UINavigationController(或UITabBarController)子类中重写它们。

此外,这也没有涵盖从横向视图推入/呈现/弹出到仅支持纵向的VC或反之的情况。 为解决这个棘手问题(从未被苹果公司解决),您应该:

在iOS <= 4和iOS >= 6中:

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

iOS 5 中:

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

这些将确实强制UIKit重新评估您的shouldAutorotate,supportedInterfaceOrientations等所有内容。


2
无论 iOS 版本如何,这些崩溃中的第一个对我来说都是不可避免的。它说在 present 完成之前不应该调用 dismiss。 - arsenius
@arsenius,你能发一段你是如何使用它的代码片段吗?只要它不是动画,就不应该有你提到的问题。 - Rafael Nobre
我也将第二个片段放到了viewDidAppear中,但它对iOS 6的情况没有任何帮助。问题在这里:http://stackoverflow.com/questions/15654339/support-rotation-in-only-one-tab-on-both-ios-5-and-6 - arsenius
抱歉,在此留下最后一条评论。将iOS 5代码移动到viewDidAppear会创建某种无限循环,输出为:“-[UIApplication beginIgnoringInteractionEvents] overflow. Ignoring.” - arsenius

3

我有一个非常好的方法,将https://dev59.com/b2cs5IYBdhLWcg3w3HoG#13982508https://dev59.com/tG025IYBdhLWcg3wPzXL#17578272混合使用。

-(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;
    }
}

并返回您希望为每个UIViewController支持的任何方向

- (NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}

1
我有一个相对复杂的通用应用程序,使用UISplitViewController和UISegmentedController,并且有一些视图必须使用presentViewController以横向方式呈现。使用上面建议的方法,我能够使iPhone ios 5&6正常工作,但出于某种原因,iPad根本不愿意以横向方式呈现。最终,我找到了一个简单的解决方案(在阅读和试错数小时后实施),适用于两个设备和ios 5&6。第1步)在控制器上指定所需的方向(与上述大致相同)。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

-(BOOL)shouldAutorotate
{
    return YES;
}

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

}

步骤二)创建一个简单的UINavigationController子类,并实现以下方法。
-(BOOL)shouldAutorotate {
        return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
        return UIInterfaceOrientationMaskLandscape;
}

步骤3)展示您的视图控制器。
vc = [[MyViewController alloc]init];
MyLandscapeNavigationController *myNavigationController = [[MyLandscapeNavigationController alloc] initWithRootViewController:vc];
[self myNavigationController animated:YES completion:nil];

希望这对某人有所帮助。

1

不想让人感到无聊,您能分享一下您的子类吗?谢谢。

编辑:好了,我终于做到了,子类非常简单。我只需要在AppDelegate中将navigationController声明为UINavigationControllerSubclass而不是默认的UINavigationController,然后修改您的子类:

- (BOOL)shouldAutorotate {
    return _shouldRotate;
}

所以我可以在viewDidLoad中调用来设置任何视图是否旋转。

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

希望这个小技巧也能帮到其他人,感谢你的提示!
提示:好好利用。
- (NSUInteger)supportedInterfaceOrientations

在您的视图控制器中,这样您就不会出现期望为纵向的视图在横向显示或反之的情况。

0

对于 Monotouch,您可以这样做:

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接