A utility class that allows for rotating/locking screen orientation anytime, anywhere.
Features:
✅ Supports rotation control in four directions:
- Portrait: Top of the phone is up
- Portrait: Top of the phone is down
- Landscape: Top of the phone is to the left
- Landscape: Top of the phone is to the right
✅ Allows control over whether the screen orientation changes automatically with device movement;
✅ Compatible with iOS 16;
✅ Supports Objective-C, Swift, and 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
}
- For iPad App, you need to go to
TARGETS
->General
->Deployment Info
-> enableRequires full screen
to use code to rotate the screen:
Methods available through the singleton ScreenRotator.shared
:
- Rotate to target orientation
func rotation(to orientation: Orientation)
- Rotate to Portrait (Top of the phone is up)
func rotationToPortrait()
- Rotate to Portrait (Top of the phone is down)
func rotationToPortraitUpsideDown()
- Rotate to Landscape (If screen is locked, rotates to left side of the phone)
func rotationToLandscape()
- Rotate to Landscape (Top of the phone is to the left)
func rotationToLandscapeLeft()
- Rotate to Landscape (Top of the phone is to the right)
func rotationToLandscapeRight()
- Toggle between Portrait and Landscape
func toggleOrientation()
- Is in Portrait mode (Top of the phone is up)
var isPortrait: Bool
- Current screen orientation (ScreenRotator.Orientation)
var orientation: Orientation
- Allow rotation to Portrait (Top of the phone is down) (Default is false)
var isAllowPortraitUpsideDown: Bool = false
- Lock screen orientation (If Portrait Orientation Lock is disabled in Control Center, setting this to
true
will prevent automatic screen rotation based on device movement)
var isLockOrientationWhenDeviceOrientationDidChange = true
// Note: Even if locked (`true`), you can still change screen orientation using this tool
- Lock Landscape orientation (If Portrait Orientation Lock is disabled in Control Center, setting this to
true
will allow screen rotation based on device movement, but only for landscape orientations)
var isLockLandscapeWhenDeviceOrientationDidChange = false
// Note: Even if locked (`true`), you can still change screen orientation using this tool
- 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
}
- 如果是iPad App,需要去
TARGETS
->General
->Deployment Info
-> 打开Requires full screen
,方可使用代码旋转屏幕:
全局使用单例ScreenRotator.shared
调用:
- 旋转至目标方向
func rotation(to orientation: Orientation)
- 旋转至竖屏(手机头在上边)
func rotationToPortrait()
- 旋转至竖屏(手机头在下边)
func rotationToPortraitUpsideDown()
- 旋转至横屏(如果锁定了屏幕,则转向手机头在左边)
func rotationToLandscape()
- 旋转至横屏(手机头在左边)
func rotationToLandscapeLeft()
- 旋转至横屏(手机头在右边)
func rotationToLandscapeRight()
- 横竖屏切换
func toggleOrientation()
- 是否正在竖屏(手机头在上边)
var isPortrait: Bool
- 当前屏幕方向(ScreenRotator.Orientation)
var orientation: Orientation
- 是否允许转向
竖屏-手机头在下边
的方向(默认不允许)
var isAllowPortraitUpsideDown: Bool = false
- 是否锁定屏幕方向(当控制中心禁止了竖屏锁定,为
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'