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

ScreenRotator

屏幕旋转工具类,能通过代码随时随地改变/保持屏幕方向。

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

Demo地址

使用效果

随时随地改变/保持屏幕方向
`push`或`present`一个跟当前方向不一样的新页面
视频的横竖屏切换

使用前提

  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)
    }  
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容