问题:
这个业务场景描述如下:项目本身支持横竖屏,但是要在特定ViewController下禁用该VC的方向,即对屏幕旋转不敏感;但又要满足能获取到当前屏幕的方向
一开始以为重写这个方法即可:
override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
super.willRotate(to: toInterfaceOrientation, duration: duration)
if toInterfaceOrientation.isPortrait {
}
if toInterfaceOrientation.isLandscape {
}
}
但运行发现会将VC的子View也旋转,界面适配强度太大遂放弃;这个方法其实在ios9及以后就被废弃了,看xcode提示使用viewWillTransitionToSize:withTransitionCoordinator:
这个回调方法;当时一想,这只是willRotate
的替代方法而已,实现后能获取屏幕方向但是肯定还是会旋转子View,重写这个方法后果然验证我的猜想。
上网搜索后看到有人在AppDelegate里面实现func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask
这个方法,用一个全局属性配合要在禁用旋转的vc生命周期里面进行控制,但遗憾的是屏幕方向是禁用的,willRotate
方法却不回调了。
网上又提到接受屏幕旋转的Notification,我猜想禁用屏幕旋转后这个通知是收不到的,果然写了一个通知方法验证后没收到。
解决:
以上思路都是根据“禁用屏幕旋转”功能后再获取回调,果不其然全都不行。突然想起十多年前智能机刚流行时很火的游戏,有不少是根据设备的运动传感器进行方向判断的,那能不能根据iOS自带的CoreMotioin框架获取到屏幕方向呢?猜想应该可以,因为当时玩游戏的时候都是把屏幕方向禁用再游玩的,但游戏仍可以获取屏幕方向进行体感操作。说干就看,半抄半写写了一个简单工具类,一试果然可以,代码如下:
import UIKit
import CoreMotion
class MotionService: NSObject {
public static var singleston: MotionService = MotionService()
private var orientationChangeBlock: ((_ rawValue: Int) -> Void)?
private var currentRawValue: Int = 0
private lazy var motionManager: CMMotionManager = {
CMMotionManager() }()
private override init() {
super.init()
initData()
}
private func initData(){
motionManager.accelerometerUpdateInterval = 1 / 10
if motionManager.isDeviceMotionAvailable {
let queue = OperationQueue()
motionManager.startDeviceMotionUpdates(to: queue) {
motion, error in
let x = motion?.gravity.x ?? 0
let y = motion?.gravity.y ?? 0
if fabs(y) >= fabs(x) {
if y >= 0 {
// UIDeviceOrientationPortraitUpsideDown
self.orientationChangeBlock?(0)
self.currentRawValue = 0
} else {
// UIDeviceOrientationPortrait
self.orientationChangeBlock?(1)
self.currentRawValue = 1
}
} else {
if x >= 0 {
// UIDeviceOrientationLandscapeRight
self.orientationChangeBlock?(2)
self.currentRawValue = 2
} else {
// UIDeviceOrientationLandscapeLeft
self.orientationChangeBlock?(3)
self.currentRawValue = 3
}
}
}
}
}
/** 使用尾随闭包简化调用,rawValue参考上面block的参数 */
public func setOnDeviceOrientationChangeListener(_ listener: ((_ rawValue: Int) -> Void)?){
orientationChangeBlock = {
rawValue in
if self.currentRawValue != rawValue {
listener?(rawValue)
}
}
}
}
注释就不写了,毕竟新手是碰不到这个层次问题,老手一看就懂。
抄的源头:Swift用CoreMotion得到屏幕当前方向的方法
使用方法:
let motionService: MotionService = MotionService.singleston
motionService.setOnDeviceOrientationChangeListener {
rawValue in
Log.warning(rawValue)
}