A utility class that allows for rotating/locking screen orientation anytime, anywhere.
Features:
✅ Control rotation in three directions:
- Portrait: Device held upright.
- Landscape: Device rotated with the top towards the left.
- Landscape: Device rotated with the top towards the right.
✅ Control whether screen orientation changes automatically with device movement.
✅ Compatible with iOS 16.
✅ Compatible with OC & Swift & SwiftUI.
✅ Simple and easy-to-use API.
- Rotating/Locking Screen Orientation Anytime, Anywhere
push
orpresent
a new page with a different orientation than the current one
- Switching between portrait and landscape modes in videos
- To globally control screen orientation with the singleton
ScreenRotator.shared
, override the following method inAppDelegate
:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return ScreenRotator.shared.orientationMask
}
-
No need to override
supportedInterfaceOrientations
andshouldAutorotate
inUIViewController
anymore. -
If you need to obtain real-time screen dimensions, override the following method in the respective
ViewController
:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Example: Portrait --> Landscape
// When the screen rotates, this function is automatically triggered by the system, and `size` represents the screen size after the rotation.
print("size \(size)") // --- (926.0, 428.0)
// Alternatively, you can also obtain the screen size after rotation through `UIScreen`.
print("mainScreen \(UIScreen.main.bounds.size)") // --- (926.0, 428.0)
// 📢 Note: If you attempt to retrieve screen-related information using `self.xxx` (such as `self.view.frame`), the size obtained at this point is still the size before the rotation.
print("----------- Screen will rotate -----------")
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)
// 📢 To obtain screen information after rotation, you need to wait until the next iteration of the `Runloop`.
DispatchQueue.main.async {
print("----------- Screen has rotated -----------")
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("==================================")
}
}
- If you need to listen for screen rotation, use
ScreenRotator.orientationDidChangeNotification
notification provided by this utility class or implement using closure:
ScreenRotator.shared.orientationMaskDidChange = { orientationMask in
// Update the orientation of the FunnyButton belonging to the window.
FunnyButton.orientationMask = orientationMask
}
Methods available through the singleton ScreenRotator.shared
:
- Rotate to a specific orientation:
func rotation(to orientation: Orientation)
- Rotate to portrait orientation:
func rotationToPortrait()
- Rotate to landscape orientation (with the top towards the left if screen lock is disabled):
func rotationToLandscape()
- Rotate to landscape orientation with the top towards the left:
func rotationToLandscapeLeft()
- Rotate to landscape orientation with the top towards the right:
func rotationToLandscapeRight()
- Toggle between portrait and landscape orientations:
func toggleOrientation()
- Check if the device is in portrait orientation:
var isPortrait: Bool
- Get the current screen orientation (ScreenRotator.Orientation):
var orientation: Orientation
- Lock/unlock screen orientation changes based on device rotation:
var isLockOrientationWhenDeviceOrientationDidChange: Bool
- Lock/unlock landscape orientation changes based on device rotation:
var isLockLandscapeWhenDeviceOrientationDidChange: Bool
- Closure to handle changes in screen orientation:
var orientationMaskDidChange: ((_ orientationMask: UIInterfaceOrientationMask) -> ())?
- Closure to handle changes in lock status for screen orientation:
var lockOrientationWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?
- Closure to handle changes in lock status for landscape orientation:
var lockLandscapeWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?
- Notification for changes in screen orientation:
ScreenRotator.orientationDidChangeNotification
- object:
orientationMask
(UIInterfaceOrientationMask)
- object:
- Notification for changes in lock status for screen orientation:
ScreenRotator.lockOrientationWhenDeviceOrientationDidChangeNotification
- object:
isLockOrientationWhenDeviceOrientationDidChange
(Bool)
- object:
- Notification for changes in lock status for landscape orientation:
ScreenRotator.lockLandscapeWhenDeviceOrientationDidChangeNotification
- object:
isLockLandscapeWhenDeviceOrientationDidChange
(Bool)
- object:
-
Objective-C: Use
JPScreenRotator
, which is specifically written in OC, with the same usage asScreenRotator
. -
SwiftUI: Use
ScreenRotatorState
to update state.- Refer to the
RotatorView
in the Demo for usage details.
- Refer to the
When push
or present
a new page with a different orientation than the current one, it is recommended to rotate first and then open after a delay of at least 0.1s to avoid screen orientation confusion. Example:
let testVC = UIViewController()
// 1. Rotate first
ScreenRotator.shared.rotation(to: .landscapeRight)
// 2. Open after a delay of at least 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)
}
}
ScreenRotator is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'ScreenRotator'
屏幕旋转工具类,能通过代码随时随地旋转/锁定屏幕方向。
Feature:
✅ 可控制旋转三个方向:
- 竖屏:手机头在上边
- 横屏:手机头在左边
- 横屏:手机头在右边
✅ 可控制是否随手机摆动自动改变屏幕方向;
✅ 适配iOS16;
✅ 兼容 OC & Swift & SwiftUI;
✅ API简单易用。
- 随时随地旋转/锁定屏幕方向
push
或present
一个跟当前方向不一样的新页面
- 视频的横竖屏切换
- 让单例
ScreenRotator.shared
全局控制屏幕方向,首先得在AppDelegate
中重写:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return ScreenRotator.shared.orientationMask
}
-
不需要再重写
UIViewController
的supportedInterfaceOrientations
和shouldAutorotate
; -
如需获取屏幕实时尺寸,在对应
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("==================================")
}
}
- 如需监听屏幕的旋转,不用再监听
UIDevice.orientationDidChangeNotification
通知,而是监听该工具类提供的ScreenRotator.orientationDidChangeNotification
通知。或者通过闭包的形式实现监听:
ScreenRotator.shard.orientationMaskDidChange = { orientationMask in
// 更新`FunnyButton`所属`window`的方向
FunnyButton.orientationMask = orientationMask
}
全局使用单例ScreenRotator.shared
调用:
- 旋转至目标方向
func rotation(to orientation: Orientation)
- 旋转至竖屏
func rotationToPortrait()
- 旋转至横屏(如果锁定了屏幕,则转向手机头在左边)
func rotationToLandscape()
- 旋转至横屏(手机头在左边)
func rotationToLandscapeLeft()
- 旋转至横屏(手机头在右边)
func rotationToLandscapeRight()
- 横竖屏切换
func toggleOrientation()
- 是否正在竖屏
var isPortrait: Bool
- 当前屏幕方向(ScreenRotator.Orientation)
var orientation: Orientation
- 是否锁定屏幕方向(当控制中心禁止了竖屏锁定,为
true
则不会【随手机摆动自动改变】屏幕方向)
var isLockOrientationWhenDeviceOrientationDidChange = true
// PS:即便锁定了(`true`)也能通过该工具类去旋转屏幕方向
- 是否锁定横屏方向(当控制中心禁止了竖屏锁定,为
true
则【仅限横屏的两个方向会随手机摆动自动改变】屏幕方向)
var isLockLandscapeWhenDeviceOrientationDidChange = false
// PS:即便锁定了(`true`)也能通过该工具类去旋转屏幕方向
- <屏幕方向>发生改变的回调闭包
var orientationMaskDidChange: ((_ orientationMask: UIInterfaceOrientationMask) -> ())?
- <是否锁定屏幕方向>发生改变的回调闭包
var lockOrientationWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?
- <是否锁定横屏方向>发生改变的回调闭包
var lockLandscapeWhenDeviceOrientationDidChange: ((_ isLock: Bool) -> ())?
- <屏幕方向>发生改变的通知:
ScreenRotator.orientationDidChangeNotification
- object:
orientationMask
(UIInterfaceOrientationMask)
- object:
- <是否锁定屏幕方向>发生改变的通知:
ScreenRotator.lockOrientationWhenDeviceOrientationDidChangeNotification
- object:
isLockOrientationWhenDeviceOrientationDidChange
(Bool)
- object:
- <是否锁定横屏方向>发生改变的通知:
ScreenRotator.lockLandscapeWhenDeviceOrientationDidChangeNotification
- object:
isLockLandscapeWhenDeviceOrientationDidChange
(Bool)
- object:
-
OC:使用专门用OC写的
JPScreenRotator
,用法和ScreenRotator
完全一致。 -
SwiftUI:可以通过
ScreenRotatorState
来更新状态。- 具体使用可以参考Demo中的
RotatorView
。
- 具体使用可以参考Demo中的
当push
或present
一个跟当前方向不一样的新页面时,建议先旋转,再延时至少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)
}
}
ScreenRotator 可通过CocoaPods安装,只需添加下面一行到你的podfile:
pod 'ScreenRotator'