我正在使用MGSplitViewController,并使用shouldAutorotateToInterfaceOrientation
来控制主视图控制器在旋转时的大小。
这在iOS 5上都正常工作,但在iOS 6上(模拟器和iPad均是如此),shouldAutorotateToInterfaceOrientation
从未被调用过。
这是我应该期望在iOS 6最终发布时修复的错误,还是有什么我不知道的改变?
我正在使用MGSplitViewController,并使用shouldAutorotateToInterfaceOrientation
来控制主视图控制器在旋转时的大小。
这在iOS 5上都正常工作,但在iOS 6上(模拟器和iPad均是如此),shouldAutorotateToInterfaceOrientation
从未被调用过。
这是我应该期望在iOS 6最终发布时修复的错误,还是有什么我不知道的改变?
请仔细阅读此内容,否则您可能会在焦虑、挣扎、颤抖中度过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;
}
注意
UIInterfaceOrientation
是 UIApplication
的一个属性,只包含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
不确定时(尤其是当我们在平坦表面上测试应用程序时),问题总是出现在应用程序的启动时。
请注意 还有另外两个带有(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
这会干扰应用程序允许的方向,并返回默认为YES
的BOOL
。
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
好的,我已经让它在 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中指定,如下所示:
或者使用代码:
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait; // etc
}
[[UIDevice currentDevice] orientation]
... UIDeviceOrientation
与UIInterfaceOrientation
不是同一件事。 - jowieif (orientation == UIDeviceOrientationUnknown)
。 - oddmeter将应用程序窗口路由控制器设置而不仅仅是将其视图作为子视图添加,对我有用(就像Rocotilos所做的那样)。
// [self.window addSubview:self.topLevelNavigationController.view];
self.window.rootViewController = self.topLevelNavigationController;
AppDelegate
已经包含您建议的行。然后,在ViewController
中覆盖shouldAutorotateToInterfaceOrientation
,设置断点并调试它。断点永远不会被触发(iOS 6)。 - Jonas Sourlier这是一个适用于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;
}
对我而言,一个快速的解决方法是在我的根视图控制器上添加以下代码
- (BOOL)shouldAutorotate {
return [self shouldAutorotateToInterfaceOrientation:self.interfaceOrientation];
}
这样做的好处是我仍然使用了 iOS 6 之前版本所使用的相同逻辑。当然,我最初已经在我的应用程序委托 didFinishLaunchingWithOptions 中正确地设置了 rootViewController,而不仅仅是添加到窗口的子视图中。
尽管许多回复中都提到了解决方法,即在iOS 6版本中实现shouldAutorotate和supportedInterfaceOrientations方法,但您还应该知道,如果您的视图控制器托管在导航控制器中,则这些方法都不起作用,因为运行时会调用UINavigationController实例上的这些方法并忽略您的代码。
显然,“解决方案”是子类化UINavigationController,并在此子类上实现这些新方法,如 iOS 6 UITabBarController supported orientation with current UINavigation controller所述。
然后,您需要修改所有使用UINavigationController的代码,以改为使用此新子类。
这必须是我见过的iOS发布中最毫无意义和烦人的破坏性变化之一。
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;
}
- (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的答案。
- (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;
}
在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
}
}