iOS 6中的shouldAutorotate:方法没有被调用

48

我已经在互联网上搜索了很久,但仍然找不到解决方法。我正在尝试使我的iOS 5应用程序与iOS 6兼容。我无法使方向控制正常工作。我无法检测到旋转即将发生的时刻。以下是我正在尝试的代码:

- (BOOL)shouldAutorotate {
    return NO;
}

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

新的supportedInterfaceOrientation方法可以正常调用,但是shouldAutorotate方法不会触发。我需要在旋转时进行一些图像交换,但是我无法得到任何指示即将发生旋转的迹象。

提前感谢。


这个问题在这里得到了解答:https://dev59.com/cWct5IYBdhLWcg3wNq0g#12580217 看看吧 :D - KarenAnne
@KarenAnne,那个答案并没有解决“shouldAutorotate”一开始就没有被调用的问题。此外,“shouldAutorotateToInterfaceOrientation”在iOS 6中已经被弃用,因此在未来不应该被视为有效选项。 - Daniel J
9个回答

80

如果在您的应用启动时出现以下错误,请查看:

应用程序窗口预期在应用程序启动结束时具有根视图控制器

如果是这种情况,修复它的方式是在AppDelegate.m文件中进行以下更改(尽管似乎有很多答案可以解决此问题):

// Replace
[self.window addSubview:[navigationController view]];  //OLD

// With
[self.window setRootViewController:navigationController];  //NEW

执行后,shouldAutoRotate 应该被正确调用。


1
我之前也遇到过同样的问题,但是通过这样做我成功解决了它:self.window.rootViewController = navigationController; (这与@user1672376的答案相同)。 - yoninja
是的,我一直在努力尝试调用shouldAutoRotate方法,但它并没有起作用。当我改变了上面的代码后,它完美地工作了。这就是我一直在寻找的正确答案。 - ashish
但是如果你没有遇到这个错误,也不想将一些随机的UIViewController设置为根视图控制器... - Daniel J

45

当我将UINavigationController用作应用程序的基础时,我使用以下子类来为我提供灵活性,以允许最顶层的子视图控制器决定旋转。

@interface RotationAwareNavigationController : UINavigationController

@end

@implementation RotationAwareNavigationController

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *top = self.topViewController;
    return top.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
    UIViewController *top = self.topViewController;
    return [top shouldAutorotate];
}

@end

5
这绝对是实现导航控制器中每个视图控制器都能拥有自己所支持的界面方向最简单、最优雅的解决方案。 - Gallonallen
这确实是一个很好的解决方案,稍加调整就可以与SplitViewControllers一起使用。 - vincentjames501
为了更清晰,创建这样的子类,然后在Storyboard Identity Inspector中将其设置为导航控制器的自定义类。 - tedyyu
非常好的答案,谢谢!应该将supportedInterfaceOrientations的返回类型从NSUInteger更改为UIInterfaceOrientationMask以消除警告。 - Patrick Domegan
请注意:要在iPad上运行此程序,您需要退出多任务处理:<stackoverflow.com/a/31771093/341994> - Patrick Domegan

15

那种方法不是确定方向的正确方式。 正确的方法是 willRotateToInterfaceOrientation:duration:

应该旋转屏幕的方法(与shouldAutorotate相反)已过时,自iOS 6起将不再调用,但无论如何它都不应该像你使用的那样使用。

编辑 回应重复的反对票。 请解释为什么使用我指示的方法不是(引用OP的话)“表明即将发生旋转”的迹象。问题的内容和标题不匹配。


2
-(BOOL)shouldRotate在iOS 6中已经出现,但它从未被调用。那么如何才能拥有一个视图控制器仅在某些时候旋转(例如,拥有一个锁定按钮以防止旋转)? - Daniel Wood
14
我找到了iOS 6中为什么我的shouldAutorotate没有被调用的原因。这是因为我的视图控制器是UINavigationController的子控制器,似乎该导航控制器不像之前的行为一样将-shouldAutorotate方法委托给它的topViewController。你可以通过继承UINavigationController并重写-shouldAutorotate和-supportedIntervalOrientations方法来解决这个问题。 - Daniel Wood
1
@possen 出于好奇,你为什么这样说呢?对我来说,似乎类别会是一个更奇怪的选择,因为它会删除现有的实现并使其无法访问(编译器会产生警告)。 - borrrden
2
在苹果论坛上有一个帖子,苹果代表明确建议不要创建类别,而是应该使用子类化。https://devforums.apple.com/message/744384 - Mike M
12
这个回答完全错误,shouldAutorate没有被弃用,实际上它仅适用于iOS 6.0,并且这是确定屏幕是否应该旋转的正确方法。willRotateToInterfaceOrientation:duration:在屏幕将要旋转时被调用,而不是用来确定是否应该旋转。 - Guilherme Torres Castro
显示剩余4条评论

10
如果您的viewControllerUINavigationController中的子viewController,则可以执行以下操作:
  • 子类化UINavigationController
  • 覆盖您的子类中的shouldAutoRotate方法
  • 当调用该方法时,请向您的topViewController发送此消息

// 此方法位于您的UINavigationController子类中

- (BOOL)shouldAutorotate
{
    if([self.topViewController respondsToSelector:@selector(shouldAutorotate)])
    {
        return [self.topViewController shouldAutorotate];
    }
    return NO;
}
  • 现在您的视图控制器将分别响应此方法。
  • 请注意,您也可以使用其他方向方法执行相同操作

10

在 iOS 6 上,容器导航控制器旋转时不会查询其子视图控制器:

iOS 6 发布说明 中:

现在,iOS 容器(如 UINavigationController)不会查询其子级以确定它们是否应自动旋转。默认情况下,iPad 界面和视图控制器的支持的界面方向设置为 UIInterfaceOrientationMaskAll,而 iPhone 界面则设置为 UIInterfaceOrientationMaskAllButUpsideDown。

可以很容易地测试这种行为。我所做的是使用相同的自定义视图控制器:

  1. 第一种情况作为主视图控制器
  2. 第二种情况作为 UIPageViewController 的子视图

在第一种情况下,通过组合 shouldAutorotatesupportedInterfaceOrientations 决定所有事项,因为 supportedInterfaceOrientations 与应用程序的支持方向相一致。

在第二种情况下,即使 UIPageViewController 调用自定义视图控制器的 supportedInterfaceOrientations,返回值也会被忽略。如果这两个方法在 UIPageViewController 的子类中被重写,则可以解决这个问题。但是,由于这个类不应该被子类化,我不确定是否会有副作用。


1
当您的应用程序启动时,我也遇到了以下错误。
“应用程序窗口在应用程序启动结束时需要根视图控制器。”
我正在使用一个UISplitViewController *splitViewController。
如果是这样,解决方法是在AppDelegate.m文件中进行以下更改:
替换
 [self.window addSubview:[splitViewController view]];

带着

[self.window setRootViewController:splitViewController];

在此之后,shouldAutoRotate 被调用并正常工作。

1
我正在使用iOS 7,但我相信我的情况可能对他人有所帮助。
我的视图控制器层次结构很深,以UITabBarController为根。应该Autorotate被调用的唯一地方是在UITabBarController内部。因此,我简单地子类化UITabBarController,并将我的旋转控制逻辑放在shouldAutorotate方法中。

0

这是我会做的方式

如果你想检查当前方向,那么请在你的viewconrtoller.m文件中添加这行代码

 #define isPortrait [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown

然后在您想要检查方向的位置,编写以下条件:

- (void)viewDidLoad
{

        if(isPortrait)
        {
            //portrait mode....

            NSLog(@"its in IsPotraitMode");
        }
        else
        {
            //landscape mode....
            NSLog(@"its in IsLandscapeMode");
        }
}

不,viewDIDLoad 太早了:视图控制器还不知道它的视图。在 viewWillAppear 中完成,在此时 self.view 知道它的当前框架。 - Bill Cheswick

0
如果您将UINavigationController作为应用程序的基础。我创建了一个UINavigationController类别,并将其命名为“UINavigationController+autoRotate”。 请将以下内容放入您的UINavigationController+autoRotate.h文件中:
#import <UIKit/UIKit.h>

@interface UINavigationController (autoRotate)

-(BOOL)shouldAutorotate;
-(NSUInteger)supportedInterfaceOrientations;

@end

将以下代码放入UINavigationController+autoRotate.m文件中:
#import "UINavigationController+autoRotate.h"

@implementation UINavigationController (autoRotate)

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

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

@end

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