iOS屏幕旋转那些事

本文屏幕旋转方案仅限于兼容iOS8+

1. 从APP层次谈起

APP常见的布局层次如下图所示:
![app layer.png](http://alithink.com/img/5rotate/app layer.png)
当工程目标开启了多个屏幕方向之后,具体类中有关屏幕旋转的配置其实只与当前屏幕展示模块的最外层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模式的场景并不适用,需要结合以上方案综合解决。

Leave a Comment

Your email address will not be published. Required fields are marked *