2011-01-01 10 views
27

を無視。iOSデバイスの向きが、私はiPhoneが現在である方向を照会する姿勢ロック

[UIDevice currentDevice].orientation 

を使用している限り、デバイスは、配向ロックされていないと動作します。ただし、ロックされている場合は、デバイスの実際の向きではなく、ロックされた向きで常に応答します。

実際のデバイスの向きを得るための高度な方法はありますか?

答えて

9

その機能は正しいです。ロックされていても常にデバイスの向きが返ってきた場合、向きの変更された通知が発生します。これはロックの目的を破るでしょう。

質問に答えるには、プライベートAPIを使用せずに、加速度計から生の値を読み取る方法がありません。

編集:

ドキュメントを確認した後、向きがロックされている場合でも、UIAccelerometerクラスは、このデータを提供しているようです。この変更はiOS 4以降で適用されました。このデータを使用することはできますが、それを処理して方向を判断する必要があります。これは、変更を常に監視して古い値と比較する必要があるため、簡単な作業ではありません。

また、モーションイベントの処理については、this guideを参照してください。これにより、向きを決定する別のルートが提供される場合があります。

+1

+1、あなたは単に加速度計を監視し、使用せずに向きを自分で決定しようとする可能性がありプライベートAPIを使用していますが、それはやや難点です。 (ロックされたことは結局はロックされています。) –

+1

スクリーンオリエンテーションシステムとは別に動作する加速度計を監視できない理由はわかりません。 – BastiBen

+1

私はあなたの最初の陳述に同意しません。デバイスオリエンテーション通知は、インターフェイスではなくデバイスの向きを通知しています。理論的には、回転ロックに関係なく通知される必要があります。 –

5

UIAccelerometerクラスは、デバイスの向きがロックされている場合でも引き続き機能します。変数を方向の値に変換する独自の方法を工夫する必要がありますが、特に複雑ではありません。

アップルのAcceleromoterGraphサンプルアプリで遊んで、加速度計の出力値が異なる方向を確認してください。

8

UIAccelerometerProtocolをサポートするようにView Controllerなどを設定して、変更をリッスンします(10hzに設定できます)。

#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を定義する必要があります。

+0

このコードは大変申し訳ございません。あなたはジャイロスコープを使ってUIDeviceOrientationFaceDown/UIDeviceOrientationFaceUpをどうやってチェックしますか? –

+0

これは良いコードですが、deviceOrientationとinterfaceOrientationの違いをわかりにくくすることは恥ずかしいと思います。 – Jef

28

また、あなたはオリエンテーション検出アルゴリズムCoreMotion

を使用することができます:ABS(Y)< ABS(x)は、あなたのiPhoneを横位置にあるが、右の を検出するために、xの符号を見れば

  • か左

  • あなたの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() 
} 
+0

驚くべき簡単な答え...完璧に働いています。ありがとうございました! –

+0

UIAccelerometerは廃止され、Swiftでは使用できません。 –

+0

携帯電話の表をテーブルの上または下に置いた場合、どのように向きを知っていますか?これは、xとyの両方の値が0になります。私はこれがジャイロセンサーのデータからのいくつかのデータで解決できると思います。 – FlySoFast

1

CMMotionManagerの使用は、上記の方法で役立ちますが、ないかもしれません。上記のロジックは安定したものではありません。私は完全にテストした結果、acceleration.x/y/zの値を見ることで向きを決定するのに役立たないことがわかりました。これは、私を助けていませんが、 if(fabs(angle<=45)currOrientation=UIDeviceOrientationLandscapeRight; else if((fabs(angle)>45)&&(fabs(angle)<135))currOrientation=((angle>0)?UIDeviceOrientationPortraitUpsideDown:UIDeviceOrientationPortrait); else currOrientation = UIDeviceOrientationLandscapeLeft;

これは、誰かのための便利来るかもしれない -

代わりに、私は姿勢WRTにfloat angle = (atan2(accelerometerData.acceleration.y,accelerometerData.acceleration.x))*180/M_PI;

と向きのため すなわち角度を見つける方法を、得ました他の2つの向き、すなわちUIDeviceOrientationFaceUp & UIDeviceOrientationFaceDownを探します。

1

私のソリューションcoremotionを使用して、デバイスが彼の方向がロックされている場合でも動作します。やったloadメソッド

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") 
    } 

let motionManager: CMMotionManager = CMMotionManager() 

ことができます願っています。

5

は、私たちはしばしば約FaceUp/FaceDownの向きを気にしませんが、彼らはまだ重要だすべての6つの方向

の取り扱い。

これらを考慮すると、方位変化の感度がはるかに適切になりますが、それらを放置すると準安定性&ヒステリシスにつながる可能性があります。

は、ここで私はそれを取り扱う方法です -

- (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]; 
       }); 
      } 
     } 
    }]; 
} 

をさらに、私は(代わりに45.0の)40.0のthreshold価値を追加しました。これにより、変化が小さくなり、変曲点でのヒステリシスが防止されます。

あなたが唯一の主要4つの向きの変化に反応するようにしたい場合は、ちょうどここSatachitoの偉大な答えを使って、この

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

を行うと、デバイスは、フェイスアップや

を伏せている場合も検出しますコードですマイナーなポイントとして
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() 
} 
関連する問題