【问题标题】:iOS device orientation disregarding orientation lockiOS设备方向无视方向锁定
【发布时间】:2011-01-01 16:28:44
【问题描述】:

我想查询 iPhone 当前所处的方向。使用

[UIDevice currentDevice].orientation

只要设备没有方向锁定就可以工作。但是,如果它被锁定,它总是以锁定的方向响应,而不是设备的实际方向。

有没有获得实际设备方向的高级方法?

【问题讨论】:

    标签: iphone objective-c ios orientation


    【解决方案1】:

    你也可以使用 CoreMotion

    方向检测算法:

    • 如果 abs( y )

    • 如果您的 iPhone 处于纵向位置,请查看 y 符号以检测向上或倒置。

    • 如果您对正面或向下感兴趣,请查看 z 值。


    import CoreMotion
    

    var uMM: CMMotionManager!
    
    override func
    viewWillAppear( p: Bool ) {
        super.viewWillAppear( p )
        uMM = CMMotionManager()
        uMM.accelerometerUpdateInterval = 0.2
    
        //  Using main queue is not recommended. So create new operation queue and pass it to startAccelerometerUpdatesToQueue.
        //  Dispatch U/I code to main thread using dispach_async in the handler.
        uMM.startAccelerometerUpdatesToQueue( NSOperationQueue() ) { p, _ in
            if p != nil {
                println(
                    abs( p.acceleration.y ) < abs( p.acceleration.x )
                    ?   p.acceleration.x > 0 ? "Right"  :   "Left"
                    :   p.acceleration.y > 0 ? "Down"   :   "Up"
                )
            }
        }
    }
    
    override func
    viewDidDisappear( p: Bool ) {
        super.viewDidDisappear( p )
        uMM.stopAccelerometerUpdates()
    }
    

    【讨论】:

    • 一个惊人的简单答案......完美运行。谢谢!
    • 很好的答案,因为 UIAccelerometer 现在已被弃用,不能在 Swift 中使用。
    • 如果您将手机正面朝上或朝下放在桌子上,您如何知道方向?它的 x 和 y 值都为 0。我认为这可以通过陀螺仪传感器数据中的一些数据来解决。
    • 加速结构有z值。您只需要检查 z 的符号即可。
    【解决方案2】:

    该功能是正确的。如果它总是返回设备方向,即使它被锁定,方向更改通知也会触发。这会破坏锁定的目的。

    要回答您的问题,如果不使用私有 API,就无法从加速度计读取原始值。

    编辑:

    查看文档后,UIAccelerometer 类似乎提供了此数据,即使在方向被锁定时也是如此。此更改适用于 iOS 4 及更高版本。即使您可以使用这些数据,您仍然需要对其进行处理以确定方向。这不是一件容易的事,因为您需要不断监控更改并将它们与旧值进行比较。

    另外,请查看 this guide 以处理运动事件。这可能会为您提供另一种确定方向的途径。

    【讨论】:

    • +1 作为一个小问题,您可以简单地监控加速度计并尝试自己确定方向而不使用私有 API,但这有点离题。 (锁定的意思毕竟是锁定的。)
    • 我不明白为什么你不能监控加速度计,它与屏幕方向系统分开工作。
    • 我不同意你的第一句话。设备方向通知是通知设备的方向而不是接口。理论上,无论旋转锁定如何,都应该通知它。
    • 这个游戏:itunes.apple.com/us/app/superbrothers-sword-sworcery/… 以一种很好的方式无视方向锁定。
    【解决方案3】:

    设置您的视图控制器或任何支持 UIAccelerometerProtocol 的东西,并开始监听变化(您可以将其设置为 10 hz)。

    #define kAccelerometerFrequency        10.0 //Hz
    -(void)viewDidAppear:(BOOL)animated {
        DLog(@"viewDidAppear");
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
        UIAccelerometer* a = [UIAccelerometer sharedAccelerometer];
        a.updateInterval = 1 / kAccelerometerFrequency;
        a.delegate = self;
    }
    
    -(void)viewWillDisappear:(BOOL)animated {
        DLog(@"viewWillDisappear");
        UIAccelerometer* a = [UIAccelerometer sharedAccelerometer];
        a.delegate = nil;
        [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    }
    
    #ifdef DEBUG
    +(NSString*)orientationToText:(const UIInterfaceOrientation)ORIENTATION {
        switch (ORIENTATION) {
            case UIInterfaceOrientationPortrait:
                return @"UIInterfaceOrientationPortrait";
            case UIInterfaceOrientationPortraitUpsideDown:
                return @"UIInterfaceOrientationPortraitUpsideDown";
            case UIInterfaceOrientationLandscapeLeft:
                return @"UIInterfaceOrientationLandscapeLeft";
            case UIInterfaceOrientationLandscapeRight:
                return @"UIInterfaceOrientationLandscapeRight";
        }
        return @"Unknown orientation!";
    }
    #endif
    
    #pragma mark UIAccelerometerDelegate
    -(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
        UIInterfaceOrientation orientationNew;
        if (acceleration.x >= 0.75) {
            orientationNew = UIInterfaceOrientationLandscapeLeft;
        }
        else if (acceleration.x <= -0.75) {
            orientationNew = UIInterfaceOrientationLandscapeRight;
        }
        else if (acceleration.y <= -0.75) {
            orientationNew = UIInterfaceOrientationPortrait;
        }
        else if (acceleration.y >= 0.75) {
            orientationNew = UIInterfaceOrientationPortraitUpsideDown;
        }
        else {
            // Consider same as last time
            return;
        }
    
        if (orientationNew == orientationLast)
            return;
    
        NSLog(@"Going from %@ to %@!", [[self class] orientationToText:orientationLast], [[self class] orientationToText:orientationNew]);
        orientationLast = orientationNew;
    }
    #pragma mark -
    

    您需要将UIInterfaceOrientation orientationLast 定义为成员变量并设置好。

    【讨论】:

    • 抱歉打扰了,很棒的代码。您将如何使用陀螺仪检查 UIDeviceOrientationFaceDown / UIDeviceOrientationFaceUp ...?
    • 这是很好的代码,但我认为你混淆/混淆了 deviceOrientation 和 interfaceOrientation 之间的差异是一种耻辱..
    【解决方案4】:

    处理所有 6 个方向

    虽然我们不经常关心 FaceUp / FaceDown 方向,但它们仍然很重要。

    将它们考虑在内会导致对方向变化的敏感度更高,而将它们排除在外会导致亚稳态和滞后。

    这是我的处理方式-

    - (void)startMonitoring
    {
        [self.motionManager startAccelerometerUpdatesToQueue:self.opQueue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
    
            if (error != nil)
            {
                NSLog(@"Accelerometer error: %@", error);
            }
            else
            {
                float const threshold = 40.0;
    
                BOOL (^isNearValue) (float value1, float value2) = ^BOOL(float value1, float value2)
                {
                    return fabsf(value1 - value2) < threshold;
                };
    
                BOOL (^isNearValueABS) (float value1, float value2) = ^BOOL(float value1, float value2)
                {
                    return isNearValue(fabsf(value1), fabsf(value2));
                };
    
                float yxAtan = (atan2(accelerometerData.acceleration.y, accelerometerData.acceleration.x)) * 180 / M_PI;
                float zyAtan = (atan2(accelerometerData.acceleration.z, accelerometerData.acceleration.y)) * 180 / M_PI;
                float zxAtan = (atan2(accelerometerData.acceleration.z, accelerometerData.acceleration.x)) * 180 / M_PI;
    
                UIDeviceOrientation orientation = self.orientation;
    
                if (isNearValue(-90.0, yxAtan) && isNearValueABS(180.0, zyAtan))
                {
                    orientation = UIDeviceOrientationPortrait;
                }
                else if (isNearValueABS(180.0, yxAtan) && isNearValueABS(180.0, zxAtan))
                {
                    orientation = UIDeviceOrientationLandscapeLeft;
                }
                else if (isNearValueABS(0.0, yxAtan) && isNearValueABS(0.0, zxAtan))
                {
                    orientation = UIDeviceOrientationLandscapeRight;
                }
                else if (isNearValue(90.0, yxAtan) && isNearValueABS(0.0, zyAtan))
                {
                    orientation = UIDeviceOrientationPortraitUpsideDown;
                }
                else if (isNearValue(-90.0, zyAtan) && isNearValue(-90.0, zxAtan))
                {
                    orientation = UIDeviceOrientationFaceUp;
                }
                else if (isNearValue(90.0, zyAtan) && isNearValue(90.0, zxAtan))
                {
                    orientation = UIDeviceOrientationFaceDown;
                }
    
                if (self.orientation != orientation)
                {
                    dispatch_async(dispatch_get_main_queue(), ^{
    
                        [self orientationDidChange:orientation];
                    });
                }
            }
        }];
    }
    

    此外,我添加了 threshold 值 40.0(而不是 45.0)。这使得变化不那么敏感,防止了拐点处的滞后。

    如果您只想对主要 4 个方向的变化做出反应,就这样做

    if (UIDeviceOrientationIsPortrait(orientation) || UIDeviceOrientationIsLandscape(orientation))
    {
         // Do something
    }
    

    【讨论】:

      【解决方案5】:

      当设备方向被锁定时,UIAccelerometer 类继续工作。您必须制定自己的方法将其变量转换为方向值,但它不应该特别复杂。

      试一试 Apple 的 AcceleromoterGraph 示例应用,看看加速度计在不同方向上输出的值。

      【讨论】:

        【解决方案6】:

        我使用 coremotion 的解决方案,即使设备锁定了他的方向,它也可以工作。

            let motionManager: CMMotionManager = CMMotionManager()
        

        关于加载方法

        motionManager.deviceMotionUpdateInterval = 0.01
            if motionManager.accelerometerAvailable{
                let queue = NSOperationQueue()
                motionManager.startAccelerometerUpdatesToQueue(queue, withHandler:
                    {data, error in
        
                        guard let data = data else{
                            return
                        }
                        let angle = (atan2(data.acceleration.y,data.acceleration.x))*180/M_PI;
        
                        print(angle)
                        if(fabs(angle)<=45){
                            self.orientation = AVCaptureVideoOrientation.LandscapeLeft
                            print("landscape left")
                        }else if((fabs(angle)>45)&&(fabs(angle)<135)){
        
                            if(angle>0){
                                self.orientation = AVCaptureVideoOrientation.PortraitUpsideDown
                                print("portrait upside Down")
        
        
                            }else{
                                self.orientation = AVCaptureVideoOrientation.Portrait
                                print("portrait")
        
                            }
                        }else{
                            self.orientation = AVCaptureVideoOrientation.LandscapeRight
                            print("landscape right")
        
                        }
        
        
                    }
                )
            } else {
                print("Accelerometer is not available")
            }
        

        希望对你有帮助。

        【讨论】:

          【解决方案7】:

          大部分答案都是使用加速度计,即整体加速度 = 用户+重力。

          但要获得设备方向,使用重力加速度会更准确。当用户在特定方向移动时,使用重力将防止边缘情况。要访问重力,我们必须改用startDeviceMotionUpdates API。

          let motionManager = CMMotionManager()
          motionManager.startDeviceMotionUpdates(to: OperationQueue()) { (data, error) in
              guard let gravity = data?.gravity else { return }
          
              let newDeviceOrientation: UIDeviceOrientation
              if abs(gravity.y) < abs(gravity.x) {
                  newDeviceOrientation = gravity.x > 0 ? .landscapeRight : .landscapeLeft
              } else {
                  newDeviceOrientation = gravity.y > 0 ? .portraitUpsideDown : .portrait
              }
          }
          

          【讨论】:

            【解决方案8】:

            使用CMMotionManager 可能会有所帮助,但不是上述方式。上述逻辑不是一个稳定的逻辑。我进行了彻底的测试,发现通过查看acceleration.x/y/z 的值并不能帮助确定方向。

            相反,我有一种方法可以找到 WRT 角度的方向,即 float angle = (atan2(accelerometerData.acceleration.y,accelerometerData.acceleration.x))*180/M_PI;

            对于方向,- if(fabs(angle<=45)currOrientation=UIDeviceOrientationLandscapeRight; else if((fabs(angle)>45)&&(fabs(angle)<135))currOrientation=((angle>0)?UIDeviceOrientationPortraitUpsideDown:UIDeviceOrientationPortrait); else currOrientation = UIDeviceOrientationLandscapeLeft;

            这对某人来说可能会派上用场,尽管这并不能帮助我找到其他 2 个方向,即UIDeviceOrientationFaceUpUIDeviceOrientationFaceDown

            【讨论】:

              【解决方案9】:

              在这里使用 Satachito 的最佳答案是代码,它还将检测设备是正面朝上还是正面朝下

              import CoreMotion
              

              var mm: CMMotionManager!
              
              init() {
                  self.mm = CMMotionManager()
                  self.mm.accelerometerUpdateInterval = 0.2
              }
              
              public func startOrientationUpdates() {
                  //  Using main queue is not recommended. So create new operation queue and pass it to startAccelerometerUpdatesToQueue.
                  //  Dispatch U/I code to main thread using dispach_async in the handler.
                  self.mm.startAccelerometerUpdates( to: OperationQueue() ) { p, _ in
                      if let p = p {
                          if(p.acceleration.x > -0.3 && p.acceleration.x < 0.3 && p.acceleration.z < -0.95) {
                              print("face up")
                          }
                          else if(p.acceleration.x > -0.3 && p.acceleration.x < 0.3 && p.acceleration.z > 0.95) {
                              print("face down")
                          }
                          else {
                              print(
                                  abs( p.acceleration.y ) < abs( p.acceleration.x )
                                      ?   p.acceleration.x > 0 ? "Right"  :   "Left"
                                      :   p.acceleration.y > 0 ? "Down"   :   "Up"
                              )
                          }
              
                      }
                  }
              }
              
              public func endOrientationUpdates() {
                  self.mm.stopAccelerometerUpdates()
              }
              

              【讨论】:

                【解决方案10】:

                以下是检测设备旋转并返回 UIDeviceOrientation 的示例。 此解决方案使用 CoreMotion,适用于所有情况。

                示例

                let orientationManager = APOrientationManager()
                orientationManager.delegate = self
                /// start detect rotation
                orientationManager.startMeasuring()
                
                /// get current interface orientation
                let orientation = orientationManager.currentInterfaceOrientation()
                print(orientation.rawValue)
                
                /// stop detect rotation
                orientationManager.stopMeasuring()
                orientationManager.delegate = nil
                

                符合委托

                extension ViewController: APOrientationManagerDelegate {
                    func didChange(deviceOrientation: UIDeviceOrientation) {
                        /// update UI in main thread
                    }
                }
                

                APOrientationManager.swift

                import Foundation
                import CoreMotion
                import AVFoundation
                import UIKit
                
                protocol APOrientationManagerDelegate: class {
                    func didChange(deviceOrientation: UIDeviceOrientation)
                }
                
                class APOrientationManager {
                
                    private let motionManager = CMMotionManager()
                    private let queue = OperationQueue()
                    private var deviceOrientation: UIDeviceOrientation = .unknown
                    weak var delegate: APOrientationManagerDelegate?
                
                    init() {
                        motionManager.accelerometerUpdateInterval = 1.0
                        motionManager.deviceMotionUpdateInterval = 1.0
                        motionManager.gyroUpdateInterval = 1.0
                        motionManager.magnetometerUpdateInterval = 1.0
                    }
                
                    func startMeasuring() {
                        guard motionManager.isDeviceMotionAvailable else {
                            return
                        }
                        motionManager.startAccelerometerUpdates(to: queue) { [weak self] (accelerometerData, error) in
                            guard let strongSelf = self else {
                                return
                            }
                            guard let accelerometerData = accelerometerData else {
                                return
                            }
                
                            let acceleration = accelerometerData.acceleration
                            let xx = -acceleration.x
                            let yy = acceleration.y
                            let z = acceleration.z
                            let angle = atan2(yy, xx)
                            var deviceOrientation = strongSelf.deviceOrientation
                            let absoluteZ = fabs(z)
                
                            if deviceOrientation == .faceUp || deviceOrientation == .faceDown {
                                if absoluteZ < 0.845 {
                                    if angle < -2.6 {
                                        deviceOrientation = .landscapeRight
                                    } else if angle > -2.05 && angle < -1.1 {
                                        deviceOrientation = .portrait
                                    } else if angle > -0.48 && angle < 0.48 {
                                        deviceOrientation = .landscapeLeft
                                    } else if angle > 1.08 && angle < 2.08 {
                                        deviceOrientation = .portraitUpsideDown
                                    }
                                } else if z < 0 {
                                    deviceOrientation = .faceUp
                                } else if z > 0 {
                                    deviceOrientation = .faceDown
                                }
                            } else {
                                if z > 0.875 {
                                    deviceOrientation = .faceDown
                                } else if z < -0.875 {
                                    deviceOrientation = .faceUp
                                } else {
                                    switch deviceOrientation {
                                    case .landscapeLeft:
                                        if angle < -1.07 {
                                            deviceOrientation = .portrait
                                        }
                                        if angle > 1.08 {
                                            deviceOrientation = .portraitUpsideDown
                                        }
                                    case .landscapeRight:
                                        if angle < 0 && angle > -2.05 {
                                            deviceOrientation = .portrait
                                        }
                                        if angle > 0 && angle < 2.05 {
                                            deviceOrientation = .portraitUpsideDown
                                        }
                                    case .portraitUpsideDown:
                                        if angle > 2.66 {
                                            deviceOrientation = .landscapeRight
                                        }
                                        if angle < 0.48 {
                                            deviceOrientation = .landscapeLeft
                                        }
                                    case .portrait:
                                        if angle > -0.47 {
                                            deviceOrientation = .landscapeLeft
                                        }
                                        if angle < -2.64 {
                                            deviceOrientation = .landscapeRight
                                        }
                                    default:
                                        if angle > -0.47 {
                                            deviceOrientation = .landscapeLeft
                                        }
                                        if angle < -2.64 {
                                            deviceOrientation = .landscapeRight
                                        }
                                    }
                                }
                            }
                            if strongSelf.deviceOrientation != deviceOrientation {
                                strongSelf.deviceOrientation = deviceOrientation
                                strongSelf.delegate?.didChange(deviceOrientation: deviceOrientation)
                            }
                        }
                    }
                
                    func stopMeasuring() {
                        motionManager.stopAccelerometerUpdates()
                    }
                
                    func currentInterfaceOrientation() -> AVCaptureVideoOrientation {
                        switch deviceOrientation {
                        case .portrait:
                            return .portrait
                        case .landscapeRight:
                            return .landscapeLeft
                        case .landscapeLeft:
                            return .landscapeRight
                        case .portraitUpsideDown:
                            return .portraitUpsideDown
                        default:
                            return .portrait
                        }
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2015-11-13
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-08-16
                  • 1970-01-01
                  • 2014-03-17
                  • 1970-01-01
                  相关资源
                  最近更新 更多