在iOS 6中,shouldAutorotateToInterfaceOrientation方法没有被调用。

66

我正在使用MGSplitViewController,并使用shouldAutorotateToInterfaceOrientation来控制主视图控制器在旋转时的大小。

这在iOS 5上都正常工作,但在iOS 6上(模拟器和iPad均是如此),shouldAutorotateToInterfaceOrientation从未被调用过。

这是我应该期望在iOS 6最终发布时修复的错误,还是有什么我不知道的改变?


请查看这个不错的解决方案 - DanSkeel
17个回答

131

请仔细阅读此内容,否则您可能会在焦虑、挣扎、颤抖中度过1-2天的时间,并像动物园里的黑猩猩抓住游客手机一样摇晃、扭动测试设备!迟早会发生...承诺 :)

iOS 6中

shouldAutorotateToInterfaceOrientation:

已被弃用,替换为

shouldAutorotate

这意味着iOS 6将永远不会调用shouldAutorotateToInterfaceOrientation

所以,如果你在你的应用程序中使用了以下代码

iOS 6之前(包括iOS5、iOS4等)

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
// your code for portrait mode

}

return YES;
}

在iOS 6+之后,你应该使用:

- (BOOL)shouldAutorotate {

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

if (orientation == UIInterfaceOrientationPortrait) {
// your code for portrait mode

}

return YES;
}

注意

UIInterfaceOrientationUIApplication 的一个属性,只包含4种可能性,对应于状态栏的方向:

UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,

UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,

UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,

UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft

请勿将其与UIDevice类的属性UIDeviceOrientation混淆,它包含7个可能的值:


UIDeviceOrientationUnknown - Can not be determined

UIDeviceOrientationPortrait - Home button facing down

UIDeviceOrientationPortraitUpsideDown - Home button facing up

UIDeviceOrientationLandscapeLeft - Home button facing right

UIDeviceOrientationLandscapeRight - Home button facing left

UIDeviceOrientationFaceUp - Device is flat, with screen facing up

UIDeviceOrientationFaceDown - Device is flat, with screen facing down

理论上你可以使用 UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];,它返回设备的实际方向 UIDeviceOrientation,但是你需要知道 UIDeviceOrientation 并不总是等于 UIInterfaceOrientation!!! 例如,当你的设备放在平面桌面上时,你可能会收到意外的值。

你也可以使用 UIInterfaceOrientation orientation = self.interfaceOrientation;,它返回界面的当前方向 UIInterfaceOrientation,但是它是 UIViewController 的属性,所以你只能在UIViewController类中访问它。

如果你想支持早期iOS6(iOS3/4/5)和iOS6设备 - 这可能是显而易见的 - 只需在代码中同时使用shouldAutorotateToInterfaceOrientation:shouldAutorotate

iOS 6开始,设备在应用程序启动期间检查三个级别和三个步骤,如果你愿意,你需要控制这些检查。

1. Info.plist - Supported Interface Orientations

在“摘要”选项卡中,您可以以图形方式设置允许的方向顺序,但其顺序非常重要。您可以通过编辑 info.plist 文件手动更改方向顺序,并且设备将在应用启动时选择第一个!这很关键,因为当存在机会使 [UIDevice currentDevice].orientation 不确定时(尤其是当我们在平坦表面上测试应用程序时),问题总是出现在应用程序的启动时。

plist setting in XCode (Info)

请注意 还有另外两个带有(iPad)或(iPhone)扩展名的设置选项,如果使用其中任何一个,它将使用当前设备或模拟器的该设置,并忽略没有扩展名的通用设置。因此,如果您运行仅限于 iPhone 的应用程序,而意外地在 plist 文件中某处留下了“支持的界面方向 (iPad)”一行,即使没有任何数据,它也将忽略您之前在通用设置中建立的规则(例如,在我的 iPhone 示例中),并且您的应用程序可能会被拒绝,原因是“我们发现您的应用程序不符合在 iPad 上运行的要求...”,即使您的应用程序不打算在 iPhone 上使用某个方向,但 iPad 将使用它,这可能会导致意外错误,因为在提交过程中所有 iPhone 应用都必须在 iPad 上运行。

2. AppDelegate - application:supportedInterfaceOrientationsForWindow

返回一个位掩码列表,列出您想要允许的每个方向,这将覆盖info.plist设置。每次设备旋转时至少调用一次此函数。

3. Top-level view controller or RootViewController - supportedInterfaceOrientations

这会与应用程序和应用程序委托集合进行交集,必须具有非零结果以避免崩溃。每次设备旋转时至少调用一次此方法,除非在我们的控制器中安装了另一个方法:

shouldAutorotate

这会干扰应用程序允许的方向,并返回默认为YESBOOL

BE CAREFUL when you use `NavigationController`

作为您的AppDelegate中的最高控制器,就像这样:

DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
UINavigationController *navigationController=[[UINavigationController alloc] initWithRootViewController:detailViewController];
self.window.rootViewController =nil;
self.window.rootViewController = navigationController;
[self.window makeKeyAndVisible];
return YES;
在这种情况下,您需要将以下代码作为类别附加到NavigationController类中的AppDelegate中,因为这是最顶层的控制器。如果您没有制作其子类别,则无法设置方向设置的地方/代码,因此您需要强制它检查您的实际rootViewController,在这种情况下是detailViewController的取向:
@implementation UINavigationController (OrientationSettings_IOS6)

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

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

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

@end

在此之后,您可以使用iOS 6中可用的任何ViewControllers方法在您的“最顶层”ViewController(在我的示例中为detailViewController)中设置首选方向,如下所示:

1. (BOOL)shouldAutorotate

2. (NSUInteger)supportedInterfaceOrientations

3. (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation

在最后一个例子中,@implementation UINavigationController (OrientationSettings_IOS6) 中的 OrientationSettings_IOS6 是什么? - craigk
@craigk - 这是一个类别定义,您可以使用任何名称,它是随意的。使用类别,您可以将方法附加到其他类,特别是如果您无法访问或编辑 .m 文件。这是一种有用的现象,请在文献中查阅。 - BootMaker

56

好的,我已经让它在 iOS6 iPad 模拟器上工作了。太棒了。这是我做的:

我只是给你展示一下之前和之后的情况,应该很容易理解:

之前

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

if (interfaceOrientation==UIInterfaceOrientationPortrait) {
 // do some sh!t

}

return YES;
}

之后

- (BOOL)shouldAutorotate {

UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];

if (orientation==UIInterfaceOrientationPortrait) {
 // do some sh!t

}

return YES;
}

关于支持的方向,您可以在info.plist中指定,如下所示:

Supported Orientation pic

或者使用代码:

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait; // etc
}

编辑:经过再次考虑,如果你计划支持低版本(iOS4.3/5/5.1)以及6.0,则只需包含相同代码内容的两种方法。在模拟器中对我有效。

不确定是否使用[[UIDevice currentDevice] orientation]... UIDeviceOrientationUIInterfaceOrientation不是同一件事。 - jowie
它们看起来是一样的。 - vampirewalk
2
如果没有人想读下面的帖子,请注意设备在应用程序启动时可能不知道它的方向。因此,您可能还需要检查 if (orientation == UIDeviceOrientationUnknown) - oddmeter

20

将应用程序窗口路由控制器设置而不仅仅是将其视图作为子视图添加,对我有用(就像Rocotilos所做的那样)。

//    [self.window addSubview:self.topLevelNavigationController.view];
self.window.rootViewController = self.topLevelNavigationController;

12
在iOS 6中,这不起作用。请尝试以下操作:创建一个新的XCode项目并选择模板“Single View Application”。该模板的AppDelegate已经包含您建议的行。然后,在ViewController中覆盖shouldAutorotateToInterfaceOrientation,设置断点并调试它。断点永远不会被触发(iOS 6)。 - Jonas Sourlier

18

这是一个适用于iOS 5及更早版本的解决方案,它不需要复制您的逻辑。只需将此代码添加到您的视图控制器中,它就会从现有的shouldAutorotateToInterfaceOrientation方法生成supportedInterfaceOrientations。

-(BOOL)shouldAutorotate{
    return YES;
}

-(NSInteger)supportedInterfaceOrientations{
NSInteger mask = 0;
if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeRight])
    mask |= UIInterfaceOrientationMaskLandscapeRight;
if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeLeft])
    mask |= UIInterfaceOrientationMaskLandscapeLeft;
if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationPortrait])
    mask |= UIInterfaceOrientationMaskPortrait;
if ([self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationPortraitUpsideDown])
    mask |= UIInterfaceOrientationMaskPortraitUpsideDown;
return mask;
}

这是一个不错的想法。虽然我认为仅为新方法创建实现并不那么困难,在大多数情况下,您仍然会得到赞同,因为当您拥有一个用作所有控制器基础的唯一UIViewController子类时,这可能是最佳解决方案。 - miho
请查看更加优雅的解决方案 - DanSkeel
这不是正确的答案。如果您为UIInterfaceOrientationPortraitUp构建了特定的逻辑,则此代码什么也不做,只是遍历整个方向并不断调用shouldAutorotateToInterfaceOrientation。 - Sam B

16

对我而言,一个快速的解决方法是在我的根视图控制器上添加以下代码

- (BOOL)shouldAutorotate {
    return [self shouldAutorotateToInterfaceOrientation:self.interfaceOrientation];
}

这样做的好处是我仍然使用了 iOS 6 之前版本所使用的相同逻辑。当然,我最初已经在我的应用程序委托 didFinishLaunchingWithOptions 中正确地设置了 rootViewController,而不仅仅是添加到窗口的子视图中。


2
但是这种方法又被弃用了,对吗? - RK-
4
shouldAutorotate并没有被弃用,但shouldAutorotateToInterfaceOrientation已经被弃用了。然而,这只意味着操作系统不再调用它。如果您已经实现了它,并且它能正常工作,那么您可以自由地调用它。对于您来说,它并没有被弃用。当然,这是关于iOS6中的更改。 - JugsteR
1
shouldAutorotateToInterfaceOrientation在iOS6中已被弃用。 - Dejell
1
shouldAutorotate没有被弃用。真的是非常棒的解决方案,也是唯一一个对我有效的解决方案。 - Hossam Ghareeb
从文档中得知:interfaceOrientation是一个方便的属性,它提供了界面的当前方向,仅在视图控制器占据整个屏幕时才有意义。不要使用此属性来通知布局决策。相反,请使用statusBarOrientation属性。 - Sergey Skoblikov
显示剩余2条评论

11

尽管许多回复中都提到了解决方法,即在iOS 6版本中实现shouldAutorotate和supportedInterfaceOrientations方法,但您还应该知道,如果您的视图控制器托管在导航控制器中,则这些方法都不起作用,因为运行时会调用UINavigationController实例上的这些方法并忽略您的代码。

显然,“解决方案”是子类化UINavigationController,并在此子类上实现这些新方法,如 iOS 6 UITabBarController supported orientation with current UINavigation controller所述。

然后,您需要修改所有使用UINavigationController的代码,以改为使用此新子类。

这必须是我见过的iOS发布中最毫无意义和烦人的破坏性变化之一。


10

iOS 5

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

iOS 6

-(BOOL)shouldAutorotate{
    return YES;
}

-(NSInteger)supportedInterfaceOrientations:(UIWindow *)window{

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


    //    return UIInterfaceOrientationMaskLandscape;
    return 24;
}

11
请使用“||”运算符代替魔法数字,这样可以使其更易于阅读! - miho
1
或者UIInterfaceOrientationMaskAllButUpsideDown。 - len
@miho:NSInteger 期望一个数字。 - filou
2
我认为 supportedInterfaceOrientations:(UIWindow *)window 应该是 supportedInterfaceOrientations。 - Jeff

3
这是苹果iOS SDK XCode4.5+中的一句话(请参见UIViewController类参考,处理视图旋转):
在iOS 6中,您的应用程序支持在应用程序的Info.plist文件中定义的界面方向。 视图控制器可以覆盖supportedInterfaceOrientations方法来限制支持的方向列表。 通常,系统仅在窗口的根视图控制器或呈现以填充整个屏幕的视图控制器上调用此方法; 子视图控制器使用其父视图控制器提供的窗口部分,并且不再直接参与有关支持哪些旋转的决策。
另外,在iOS6中,UIViewController类的shouldAutorotateToInterfaceOrientation:方法已经被弃用。
因此,在您的根视图控制器中,您需要执行以下操作:
- (BOOL)shouldAutorotate {
  return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
  return UIInterfaceOrientationMaskPortrait;
}

顺便说一下,“根视图控制器”是你设置为appDelegate的窗口对象的rootViewController的任何UIViewController子类。通常情况下,您会在appDelegate的application:didFinishLaunchingWithOptions:方法中执行此操作。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  // Override point for customization after application launch.
  self.window.rootViewController = [FFDashboardController create];
  [self.window makeKeyAndVisible];
  return YES;
}

如果您想使用UINavigationController作为根视图控制器,请查看Vladimir的答案


3
作为对@Rocotilos的回应,我有一个附加说明,这发生在我的代码中,我在论坛中没有看到过类似的情况。我遇到了这样一种情况,在应用程序的生命周期的最开始阶段,方向在shouldAutorotate方法中是未知的。这导致应用程序在横向显示时无法正确显示视图。在模拟器中旋转几次后,它就正常工作了。
我的情况是,在某些弹出的视图中,我们希望使用横向布局。因此,我们不希望shouldAutorotate返回YES。我意识到这可能是一个罕见的情况,但我已经花了很多时间来诊断这个问题,并希望传递给大家。希望这会有所帮助。
- (BOOL) shouldAutorotate {

    BOOL shouldRotate = NO;
    UIInterfaceOrientation orientation = [[UIDevice currentDevice] orientation];

    if ([self IsCaseWhereWeDontWantLandscapeAutorotation]) {
        shouldRotate = NO;
    } else if (orientation == UIDeviceOrientationUnknown) {
        //Handle the case where the device's orientation is not yet known
        shouldRotate = YES;
    } else {
        //Handle the normal case
        shouldRotate = (orientation == UIInterfaceOrientationMaskLandscape);
    }

    return shouldRotate;
}

3
这对我在iOS6和Xcode 4.5 GM上的工作:

在AppDelegate.m上:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    .....

    //   self.window.rootViewController = self.navigationController;

    [window setRootViewController:navigationController];

    .....

    return YES;
}

在ViewController中:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration

{
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation))

    {
      // landscape
    }
    else
    {
      //portrait
    }
}

现有的代码在iOS6上无法运行。我的应用程序可以在5.x上运行,但在iOS 6上无法运行。 - strange
1
这些评论对我很有用:http://cocos2d-x.org/boards/6/topics/15447?r=15734 - Carlos Moura

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