1、工程中Plist、AppDelegate、TabbarController、NavigationController、ViewController这些都可以控制方向,它们之间有什么关联?

当屏幕发生旋转的时候(前提是系统的方向控制开关打开),系统是最开始询问App的plist配置,如果配置只支持一个方向就不会向下询问了。如果支持多个方向会询问window的delegate,同样如果代理方法里面只支持一个方向就不会向下询问。如果代理方法里面支持多个方向window会去询问RootController比如TabbarController。到了Controller这一级,如果不主动询问下一级的方向,下一级的控制是会被忽略的。

2、工程中Plist只支持一个方向,比如Portrait,但是通过手动设置UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")屏幕方向会变化吗?

会变化,这个方法是打破规范的一个非公开 API方法。会突破plist屏障,直接从window的AppDelegate开始。但是我们很多场景又必须用到,比如视频App,不支持根据系统方向旋转,但是又可以手动控制屏幕横屏。

3、视频App场景下的实践?

  • Plist只支持Portrait;

  • 屏蔽掉系统旋转,shouldAutorotate = false,不写默认就是;

  • AppDelegate支持可能出现的多种方向,.allButUpsideDown;

  • TabbarController,NavigationController,全部指向当前显示的Controller的方向;

  • 基类Controller,封装方向控制,开放一个变量isContentPortrait给子类使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // --- ViewController基类 ---
    open override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    navigationController?.view.backgroundColor = .white
    if isContentPortrait, !UIDevice.isPortrait {
    UIDevice.forcePortrait()
    }
    if !isContentPortrait, UIDevice.isPortrait {
    UIDevice.forceLandscapeRight()
    }
    }

    // --- UIDevice+ ---
    public extension UIDevice {
    /// 是否为竖直界面
    static var isPortrait: Bool = true
    /// 是否支持界面旋转
    static var isSupportedRotate: Bool = false
    /// 强制竖屏
    static func forcePortrait() {
    isPortrait = true
    UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
    }
    /// 强制横屏(右方向)
    static func forceLandscapeRight() {
    isPortrait = false
    UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
    UIViewController.attemptRotationToDeviceOrientation()
    }
    }
  • 需要注意,window类型弹窗等逃离controller链条的页面。可能一个横屏视频全屏页面,弹窗会变成竖直的,这里需要手动控制。如果需要继承当前页面的方向,设置如下代码即可:

    1
    2
    3
    4
    5
    6
    7
    override public func viewDidLoad() {
    super.viewDidLoad()
    modalTransitionStyle = .crossDissolve
    modalPresentationStyle = .overFullScreen
    view.backgroundColor = UIColor(hex: 0x000000, transparency: 0.65)
    isContentPortrait = UIDevice.isPortrait
    }