1. 基础概念
iOS 屏幕方向的控制,主要通过三部分控制:
Info.plist 中配置全局支持的屏幕方向
AppDelegate 提供的 application:supportedInterfaceOrientationsForWindow: 方法
各个 UIViewController 及其容器控制器的相关旋转方法
2. Info.plist 中的方向配置
UISupportedInterfaceOrientations (iPhone) 和 UISupportedInterfaceOrientations~ipad (iPad)
这里声明 app 支持哪些方向,比如只支持竖屏(Portrait),或者支持竖屏和横屏(Portrait + LandscapeLeft + LandscapeRight)
这是全局限制,决定整个应用可支持的方向范围,任何代码都不能超出这里的限制。
3. AppDelegate 中的方法
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
该方法优先级高于 Info.plist 的配置,可以动态控制整个 app 支持的方向。
如果实现了此方法,系统会调用它来决定支持的方向,默认返回 Info.plist 的配置。
如果想针对某个窗口或者某些情况特殊处理,可以在这里实现。
4. UIViewController 相关方法
这些方法决定单个控制器是否支持旋转及支持的方向范围:
- (BOOL)shouldAutorotate
返回是否允许自动旋转,返回 NO 会禁止旋转。
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
返回支持的屏幕方向掩码(如:UIInterfaceOrientationMaskPortrait)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
当控制器以模态形式展示时,初始方向。
5. 容器控制器的处理
5.1 UINavigationController
系统默认 优先使用 UINavigationController 自己的旋转方法,而不是它的子控制器。
UINavigationController 的默认实现:
shouldAutorotate 返回 YES
supportedInterfaceOrientations 返回所有方向(通常是全支持)
开发者需要重写 UINavigationController 的这些方法,将调用转发给它的 topViewController(当前显示的控制器),示例:
- (BOOL)shouldAutorotate {
return [self.topViewController shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
5.2 UITabBarController
规则与 UINavigationController 类似,默认只返回所有方向支持。
同样推荐重写上述三个方法,将权限转发给当前选中的子控制器:
- (BOOL)shouldAutorotate {
return [self.selectedViewController shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [self.selectedViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
6. 调用顺序及优先级总结
系统判断屏幕方向时先参考 Info.plist,如果不支持,方向直接被限制;
如果实现了 AppDelegate 的 application:supportedInterfaceOrientationsForWindow:,系统会调用该方法返回方向掩码,覆盖 Info.plist 的配置;
系统询问当前显示的顶层控制器是否允许旋转:
如果是容器控制器(UINavigationController、UITabBarController),默认它们自己的实现优先级高于子控制器。
但容器控制器默认实现支持所有方向,通常不符合需求。
你应该重写容器控制器的相关方法,将调用转发给子控制器。
子控制器的 shouldAutorotate 和 supportedInterfaceOrientations 生效,决定是否旋转和支持哪些方向;
模态弹出控制器展示时会调用 preferredInterfaceOrientationForPresentation 来确定初始方向;
7. 注意点
只要 shouldAutorotate 返回 NO,系统不会旋转屏幕;
supportedInterfaceOrientations 是一个位掩码,允许多个方向,比如:
UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft
如果 app 是使用模态视图弹出控制器,要确保模态控制器的方向方法正确,否则可能导致旋转异常;
旋转行为仅对设备物理旋转生效,不会影响通过代码设置旋转方向(例如使用私有 API 强制方向)。
8. 总结示意图
用户旋转设备
↓
系统查询 Info.plist 支持的方向范围
↓
系统调用 AppDelegate application:supportedInterfaceOrientationsForWindow:(如果实现)
↓
系统询问最上层控制器(如 UINavigationController 或 UITabBarController)
↓
容器控制器的 shouldAutorotate & supportedInterfaceOrientations
↓
容器控制器(如 UINavigationController)是否转发给子控制器
↓
子控制器的 shouldAutorotate & supportedInterfaceOrientations
↓
确定是否旋转,旋转到哪个方向