ScreenRotator

屏幕旋转东西类,能经过代码随时随地改动/坚持屏幕方向。

Feature:
    ✅ 可操控旋转三个方向:
        - 竖屏:手机头在上边
        - 横屏:手机头在左面
        - 横屏:手机头在右边
    ✅ 可操控是否随手机摇摆主动改动屏幕方向;
    ✅ 适配iOS16;
    ✅ 兼容 OC & Swift & SwiftUI;
    ✅ API简单易用。

Demo地址

运用效果

  • 随时随地改动/坚持屏幕方向

ScreenRotator - 屏幕旋转工具类 随时随地改变/保持屏幕方向

  • pushpresent一个跟当前方向不一样的新页面

ScreenRotator - 屏幕旋转工具类 随时随地改变/保持屏幕方向

  • 视频的反正屏切换

ScreenRotator - 屏幕旋转工具类 随时随地改变/保持屏幕方向

运用前提

  1. 让单例ScreenRotator.shared全局操控屏幕方向,首先得在AppDelegate中重写:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return ScreenRotator.shared.orientationMask
}
  1. 不需要再重写ViewControllersupportedInterfaceOrientationsshouldAutorotate

  2. 如需获取屏幕实时尺度,在对应ViewController中重写:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    // :竖屏 --> 横屏
    // 当屏幕产生旋转时,体系会主动触发该函数,`size`为【旋转之后】的屏幕尺度
    print("size \(size)") // --- (926.0, 428.0)
    // 或许经过`UIScreen`也能获取【旋转之后】的屏幕尺度
    print("mainScreen \(UIScreen.main.bounds.size)") // --- (926.0, 428.0)
    //  注意:假如想经过`self.xxx`去获取屏幕相关的信息(如`self.view.frame`),【此时】获取的尺度还是【旋转之前】的尺度
    print("----------- 屏幕行将旋转 -----------")
    print("view.size \(view.frame.size)") // - (428.0, 926.0)
    print("window.size \(view.window?.size ?? .zero)") // - (428.0, 926.0)
    print("window.safeAreaInsets \(view.window?.safeAreaInsets ?? .zero)") // - UIEdgeInsets(top: 47.0, left: 0.0, bottom: 34.0, right: 0.0)
    //  想要获取【旋转之后】的屏幕信息,需要到`Runloop`的下一个循环才干获取
    DispatchQueue.main.async {
        print("----------- 屏幕已经旋转 -----------")
        print("view.size \(self.view.frame.size)") // - (926.0, 428.0)
        print("window.size \(self.view.window?.size ?? .zero)") // - (926.0, 428.0)
        print("window.safeAreaInsets \(self.view.window?.safeAreaInsets ?? .zero)") // - UIEdgeInsets(top: 0.0, left: 47.0, bottom: 21.0, right: 47.0)
        print("==================================")
    }
}
  1. 如需监听屏幕的旋转,不必再监听UIDevice.orientationDidChangeNotification告诉,而是监听该东西类供给的ScreenRotator.orientationDidChangeNotification告诉。或许经过闭包的方式实现监听:
ScreenRotator.shard.orientationMaskDidChange = { orientationMask in
    // 更新`FunnyButton`所属`window`的方向
    FunnyButton.orientationMask = orientationMask
}

API

全局运用单例ScreenRotator.shared调用:

  1. 旋转至方针方向
func rotation(to orientation: Orientation)
  1. 旋转至竖屏
func rotationToPortrait()
  1. 旋转至横屏(假如确定了屏幕,则转向手机头在左面)
func rotationToLandscape()
  1. 旋转至横屏(手机头在左面)
func rotationToLandscapeLeft()
  1. 旋转至横屏(手机头在右边)
func rotationToLandscapeRight()
  1. 反正屏切换
func toggleOrientation()
  1. 是否正在竖屏
var isPortrait: Bool
  1. 当前屏幕方向(ScreenRotator.Orientation)
var orientation: Orientation
  1. 是否确定屏幕方向(当操控中心制止了竖屏确定,为true则不会【随手机摇摆主动改动】屏幕方向)
var isLockOrientationWhenDeviceOrientationDidChange = true
// PS:即使确定了(`true`)也能经过该东西类去旋转屏幕方向
  1. 是否确定横屏方向(当操控中心制止了竖屏确定,为true则【仅限横屏的两个方向会随手机摇摆主动改动】屏幕方向)
var isLockLandscapeWhenDeviceOrientationDidChange = false
// PS:即使确定了(`true`)也能经过该东西类去旋转屏幕方向
  1. 屏幕方向产生改动的回调
var orientationMaskDidChange: ((_ orientationMask: UIInterfaceOrientationMask) -> ())?
  1. 确定屏幕方向产生改动的回调
var lockOrientationWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?
  1. 确定横屏方向产生改动的回调
var lockLandscapeWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?

可监听的告诉

  1. 屏幕方向产生改动的告诉:
  • ScreenRotator.orientationDidChangeNotification
    • object: orientationMask(UIInterfaceOrientationMask)
  1. 确定屏幕方向产生改动的告诉:
  • ScreenRotator.lockOrientationWhenDeviceOrientationDidChangeNotification
    • object: isLockOrientationWhenDeviceOrientationDidChange(Bool)
  1. 确定横屏方向产生改动的告诉:
  • ScreenRotator.lockLandscapeWhenDeviceOrientationDidChangeNotification
    • object: isLockLandscapeWhenDeviceOrientationDidChange(Bool)

兼容 OC & SwiftUI

  • OC:运用专门用OC写的JPScreenRotator,用法和ScreenRotator完全一致。

  • SwiftUI:可以经过ScreenRotatorState来更新状态。

    • 具体运用可以参阅Demo中的RotatorView

Tips

pushpresent一个跟当前方向不一样的新页面时,建议先旋转,再延时至少0.1s才打开,否则新页面的屏幕方向会错乱。例如:

let testVC = UIViewController()
// 1.先旋转
ScreenRotator.shared.rotation(to: .landscapeRight)
// 2.延时至少0.1s再打开
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
    if let navCtr = self.navigationController {
        navCtr.pushViewController(testVC, animated: true)
    } else {
        self.present(testVC, animated: true)
    }  
}