咱们公司的 app 只支撑竖屏, 只要在视频播映的时候才能够横屏, 所以这就需求咱们强制去旋转屏幕. 我想一般的 app 大约都会有这种需求.

最近随着 iOS16 的更新, 线上的 app 在 iOS16 系统上不论用了, 原因就是苹果从 iOS16 开端, 更改了屏幕旋转的机制, 以后都要用 UIWindowScence 这个 API 类. 所以咱们的 App 就只能依据版别去做适配, 新的要支撑, 老的也要兼容.

在这儿, 我就直接上干货, 只展现重要代码, 就不写 demo, 没什么技术含量, 做为一个日常记录分享而已.

重点提示
Xcode 14.0
MacOS 12.5
手机 iOS15.1iOS16

一. AppDelegate 装备

界说一个 bool 类型的变量

全局操控否是横屏代理办法依据这个变量来回来是 竖屏 还是 横屏, iOS16 及以上能够做到依据屏幕方向适配横屏, 咱们公司要求不高, 所以咱们是强制右横屏, 这一点是不太友好, 这不是重点.

  • 这一步 SwiftObjC 没什么差异, 仅仅语法不同, 所以就只供给了 Swift 代码.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    // 界说一个 bool 类型的变量
    var isFullScreen: Bool = false
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        if isFullScreen {
            if #available(iOS 16.0, *) {
                // 16 及以上能够做到依据屏幕方向适配横屏
                return .landscape
            } else {
                // 16 以下不方便做, 所以咱们是强制 右横屏
                return .landscapeRight
            }
        }
        return .portrait
    }
}

二. 适配 iOS16 旋转屏幕

在原来基础上增加适配 iOS16 的代码 在 VC 中点击横屏按钮时进行强制屏幕旋转, 这儿强调一下, 播映器的横屏按钮操作最好是回调到当时 VC 中去操作, setNeedsUpdateOfSupportedInterfaceOrientations() 这个办法是 VC 的对象办法, 这儿相同SwiftObjC 没什么差异, 仅仅语法不同.

func switchOrientation(isFullScreen: Bool) {
        let kAppdelegate = UIApplication.shared.delegate as? AppDelegate
        kAppdelegate?.isFullScreen = isFullScreen
        // 设置屏幕为横屏
        if #available(iOS 16.0, *) {
            setNeedsUpdateOfSupportedInterfaceOrientations()
            guard let scence = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
                return
            }
            let orientation: UIInterfaceOrientationMask = isFullScreen ? .landscape : .portrait
            let geometryPreferencesIOS = UIWindowScene.GeometryPreferences.iOS(interfaceOrientations: orientation)
            scence.requestGeometryUpdate(geometryPreferencesIOS) { error in
                print("强制\(isFullScreen ? "横屏" : "竖屏" )错误: \(error)")
            }
        } else {
            let oriention: UIDeviceOrientation = isFullScreen ? .landscapeRight : .portrait
            UIDevice.current.setValue(oriention.rawValue, forKey: "orientation")
            UIViewController.attemptRotationToDeviceOrientation()
        }
        // 更新 反正屏对应的 UI 
        // ...
    }

三. 强制旋转屏幕

在播映器反正屏切换按钮的回调办法中调用 旋转屏幕办法即可, 不论手机有没有翻开自动旋转, 都能够实现屏幕方向切换.

    // 播映器 - 全屏按钮切换回调
    func playerViewRotateScreen(isFull: Bool) {
        switchOrientation(isFullScreen: isFull)
    }

四. 自动旋转

手机需求翻开自动旋转开关, 注册屏幕旋转告诉, 监听屏幕旋转时的方向. 办法不只一种, 但是我就用下面这个.

  • 一定要注意下面这两个办法, 不然有或许告诉不收效, 一个敞开一个封闭.
    • UIDevice.current.beginGeneratingDeviceOrientationNotifications()
    • UIDevice.current.endGeneratingDeviceOrientationNotifications()
  • 注意: 我这儿做的是 16 以下只支撑右横屏, 16 不需求获取设备方向, 因此能够支撑 左/右横屏. 这也是 AppDelegate 中区分版别的原因.

友谊提示 :
最好是把侧滑回来手势给禁掉.
不然横屏侧滑回来就出问题了,
当然也能够做的更精密些, 横屏时禁止.
我做验证就简略些.

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        UIDevice.current.beginGeneratingDeviceOrientationNotifications()
        NotificationCenter.default.addObserver(self, selector: #selector(screenChangedOrientation(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
        navigationController?.interactivePopGestureRecognizer?.isEnabled = false
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.interactivePopGestureRecognizer?.isEnabled = true
    }
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        NotificationCenter.default.removeObserver(self)
        UIDevice.current.endGeneratingDeviceOrientationNotifications()
    }
    // 反正屏监听
    @objc private func screenChangedOrientation(_ notification: Notification) {
        let info = notification.userInfo
        guard let animated = info?["UIDeviceOrientationRotateAnimatedUserInfoKey"] as? Int, animated == 1 else {
            return
        }
        let orientation = UIDevice.current.orientation
        if orientation == UIDeviceOrientation.landscapeLeft || orientation == UIDeviceOrientation.landscapeRight {
            // 横屏
            videoView.changeScreenOrientation(isFull: true)
        } else if orientation == UIDeviceOrientation.portrait {
            // 竖屏
            videoView.changeScreenOrientation(isFull: false)
        }
    }