【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閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容