本文屏幕旋转方案仅限于兼容iOS8+
1. 从APP层次谈起
APP常见的布局层次如下图所示:
当工程目标开启了多个屏幕方向之后,具体类中有关屏幕旋转的配置其实只与当前屏幕展示模块的最外层VC容器有关。
但由于往往存在容器中某些VC针对屏幕旋转的个性化配置,所以需要进行从里层VC到外层容器的旋转状态传递。例如:
- 里层UIViewController的配置
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return .Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return .Portrait
}
- 自定义UINavigationController的配置
override public func shouldAutorotate() -> Bool {
return self.viewControllers.last?.shouldAutorotate() ?? false
}
override public func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return self.viewControllers.last?.supportedInterfaceOrientations() ?? .Portrait
}
override public func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return self.viewControllers.last?.preferredInterfaceOrientationForPresentation() ?? .Portrait
}
- 自定义UITabBarController的配置
override func shouldAutorotate() -> Bool {
return self.selectedViewController?.shouldAutorotate() ?? false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return self.selectedViewController?.supportedInterfaceOrientations() ?? .Portrait
}
override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
return self.selectedViewController?.preferredInterfaceOrientationForPresentation() ?? .Portrait
}
2. AppDelegate配置
按照上面的方法配置好后,就可以自由的控制转屏了,但在某些情况下会存在问题,举个例子:假如A页面屏幕锁定为竖屏,点击A页面的一个按钮跳转到了B页面(方式存在push跟present两种),B页面是可以进行横竖屏旋转的,当B页面旋转至横屏,这时候点击返回,会发现A页面也变成横屏展示了,而且无法通过屏幕旋转恢复到竖屏展示。这时候下面这个代理方法就排上用场了:
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
// 限定所有present的模态视图只能竖屏
if self.window?.rootViewController?.presentedViewController != nil {
return .Portrait
}
// 限定容器中当前展示的为RotateDetailViewController的实例时可以旋屏,其余页面只能竖屏
let baseTabBarController = self.window?.rootViewController as? BaseTabBarController
if ((baseTabBarController?.selectedViewController as? BaseNavigationController)?.topViewController is RotateDetailViewController {
return .AllButUpsideDown
} else {
return .Portrait
}
}
3. 强制旋屏
前提条件:该VC允许旋转
- 强制竖屏
func forcePortrait(){
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
let isLandscape = width > height
if isLandscape {
let device = UIDevice.currentDevice()
let number = NSNumber(integer: UIInterfaceOrientation.Portrait.rawValue)
device.setValue(number, forKey: "orientation")
}
}
- 强制横屏
func forceLandScape(){
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
let isLandscape = width < height
if isLandscape {
let device = UIDevice.currentDevice()
let number = NSNumber(integer: UIInterfaceOrientation.LandscapeRight.rawValue)
device.setValue(number, forKey: "orientation")
}
}
总结
以上方案基本可以搞定大部分的屏幕旋转场景了。如果APP的产品需求中主体为固定方向,只要求对弹出(present)模态视图进行旋转的话,可以参考下面这篇文章:
iOS Orientations: Landscape orientation for only one View Controller
这么做会更加方便一些,但问题是这种方案对于push模式的场景并不适用,需要结合以上方案综合解决。