iOS: 禁用屏幕旋转功能后获取屏幕方向

问题:

这个业务场景描述如下:项目本身支持横竖屏,但是要在特定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)
}

猜你喜欢

转载自blog.csdn.net/kicinio/article/details/128606803