設備靜止時受到的地球引力為1g,1g是物體在地球的海平面上受到的下拉力(9.8米/秒²)。假如設備從高處掉落,其加速計測量到的加速度將為0g。假如設備水平放在桌面上,則加速計測量出的加速度為1g,且方向朝上。
加速計測量3個軸(x、y和z)上的值,如圖所示:
這個軸在方向上有些不同於傳統坐標軸,考慮以下實際情況:
1g重力的分布情況是:y=-1.0
1g重力的分布情況是:x=1.0
1g重力的分布情況是:z=-1.0
1g重力的分布情況是:x=0.5,y=-0.5
1g重力的分布情況是:y=-0.5,z=0.5
僅當設備的朝向相對於重力的方向發生變化時,加速計才能檢測到;要同時檢測設備的朝向和運動數據,就需要用到陀螺儀了。當查詢設備的陀螺儀時,它將報告設備繞x, y, z軸的旋轉速度,單位為弧度/秒;2弧度相當於一整圈,因此陀螺儀返回讀數2表示設備繞相應的軸每秒轉一圈。
有兩種方式訪問設備的朝向和運動數據,一種是通過UIDevice請求朝向通知,另一種是利用框架Core Motion定期地直接訪問加速計和陀螺儀數據。
通過UIDevice請求朝向通知
雖然可直接查詢加速計並使用它返回的值判斷設備的朝向,但Apple為開發人員簡化了這項工作。單例UIDevice表示當前設備,它包含方法beginGeneratingDeviceOrientationNotifications,該方法命令iOS將朝向通知發送到通知中心(NSNotificationCenter)。啟動通知后,就可以注冊一個NSNotificationCenter實例,以便設備的朝向發生變化時自動調用指定的方法。
通過訪問UIDevice的屬性orientation來獲得設備當前朝向,該屬性的類型為枚舉值UIDeviceOrientation,有6個預定義值:
UIDeviceOrientationFaceUp — 設備正面朝上
UIDeviceOrientationFaceDown — 設備正面朝下
UIDeviceOrientationPortrait — 縱向(Home鍵在下)
UIDeviceOrientationPortraitUpsideDown — 縱向倒轉(Home鍵在上)
UIDeviceOrientationLandscapeLeft — Home鍵在左邊的橫向
UIDeviceOrientationLandscapeRight — Home鍵在右邊的橫向
- (void)viewDidLoad { [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(orientationChanged:) name:@"UIDeviceOrientationDidChangeNotification"object:nil]; [super viewDidLoad]; } - (void)orientationChanged:(NSNotification *)notification { UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; NSLog(@"當前朝向枚舉數字值:%d",orientation); switch (orientation) { case UIDeviceOrientationPortrait: self.lblOriention.text = @"Portrait"; break; case UIDeviceOrientationPortraitUpsideDown: self.lblOriention.text = @"Portrait Upside Down"; break; case UIDeviceOrientationLandscapeLeft: self.lblOriention.text = @"Landscape Left"; break; case UIDeviceOrientationLandscapeRight: self.lblOriention.text = @"Landscape Right"; break; case UIDeviceOrientationFaceUp: self.lblOriention.text = @"Face Up"; break; case UIDeviceOrientationFaceDown: self.lblOriention.text = @"Face Down"; break; default: self.lblOriention.text = @"Unknown"; break; } }
代碼解釋:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
beginGeneratingDeviceOrientationNotifications從字面來理解是:開始產生設備朝向通知。實際作用是,通過調用該方法通知設備:如果用戶改變了iPhone或iPad的朝向,我們想獲悉這一點。
[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(orientationChanged:) name:@"UIDeviceOrientationDidChangeNotification"object:nil];
[NSNotificationCenter defaultCenter]調用默認的消息中心,然后向默認的消息中心添加
addObserver — 觀察者,即處理通知的類,如果是當前類,則設置為self。
selector — 收到通知后調用何種方法。
name — 通知的名字,上面的"UIDeviceOrientationDidChangeNotification"來自於UIDevice.h:
UIKIT_EXTERNNSString *constUIDeviceOrientationDidChangeNotification;
使用Core Motion讀取加速計和陀螺儀數據
利用UIDevice只能判斷極端朝向,應用程序經常要獲悉這些朝向之間的過渡狀態,如設備處於某個傾斜位置。Core Motion運動管理器讓您能夠指定從加速計和陀螺儀那里接收更新的頻率(單位為秒),還讓您能夠直接指定一個處理程序塊(handle block),每當更新就緒時都將執行該處理程序塊。
實際加速度在Core Motion里被分解成了兩部分:Gravity和UserAcceleration。Gravity代表重力1g在設備的分布情況,UserAcceleration代表設備運動中的加速度分布情況。將這兩者相加就等於實際加速度。Gravity的三個軸所受的重力加起來始終等於1g,而UserAcceleration取決於單位時間內動作的幅度大小。
CMRotationRate的X,Y,Z分別代表三個軸上的旋轉速率,單位為弧度/秒。旋轉速度為1弧度/秒,意味着設備每秒旋轉半圈。這里復習一下弧度與角度的轉換:
1角度 = π/180 弧度
1弧度 = 180/π角度
360角度 = 360 * π/180 = 2π弧度 = 一整圈
CMAttitude的三個屬性Yaw,Pitch和Roll分別代表左右擺動、俯仰以及滾動。可以將設備想象成一架飛機,下面的gif圖演示了各種運動狀態:
Yaw的運動狀態:
Pitch的運動狀態:
Roll的運動狀態:
讓應用程序使用CMMotionManager需要3個步驟:分配並初始化運動管理器→設置更新頻率→使用startDeviceMotionUpdatesToQueue:withHandler請求開始更新並將更新發送給一個處理程序塊。
首先需要將框架Core Motion添加到項目中:
下面的代碼實現了這樣一個界面,通過CMMotionManager返回了設備的各個狀態值:
#import "ViewController.h" @interface ViewController () @property (strong, nonatomic) IBOutlet UILabel *lblYaw; @property (strong, nonatomic) IBOutlet UILabel *lblPitch; @property (strong, nonatomic) IBOutlet UILabel *lblRoll; @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerX; @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerY; @property (strong, nonatomic) IBOutlet UILabel *lblAccelerometerZ; @property (strong, nonatomic) IBOutlet UILabel *lblGravityX; @property (strong, nonatomic) IBOutlet UILabel *lblGravityY; @property (strong, nonatomic) IBOutlet UILabel *lblGravityZ; @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateX; @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateY; @property (strong, nonatomic) IBOutlet UILabel *lblRotationRateZ; @property (strong, nonatomic) CMMotionManager *motionManager; - (IBAction)motionSwitchHandler:(id)sender; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.motionManager = [[CMMotionManager alloc] init]; self.motionManager.deviceMotionUpdateInterval = 1.0f/10.0f; //1秒10次 } - (void)controlHardware { [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) { //Acceleration if(fabs(motion.userAcceleration.x)>1.3f) self.lblAccelerometerX.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.x]; if(fabs(motion.userAcceleration.y)>1.3f) self.lblAccelerometerY.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.y]; if(fabs(motion.userAcceleration.z)>1.3f) self.lblAccelerometerZ.text = [NSString stringWithFormat:@"%.2f",motion.userAcceleration.z]; //Gravity self.lblGravityX.text = [NSString stringWithFormat:@"%.2f",motion.gravity.x]; self.lblGravityY.text = [NSString stringWithFormat:@"%.2f",motion.gravity.y]; self.lblGravityZ.text = [NSString stringWithFormat:@"%.2f",motion.gravity.z]; //yaw,pitch,roll self.lblYaw.text = [NSString stringWithFormat:@"%.2f",motion.attitude.yaw]; self.lblPitch.text = [NSString stringWithFormat:@"%.2f",motion.attitude.pitch]; self.lblRoll.text = [NSString stringWithFormat:@"%.2f",motion.attitude.roll]; //Gyroscope's rotationRate(CMRotationRate) self.lblRotationRateX.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.x]; self.lblRotationRateY.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.y]; self.lblRotationRateZ.text = [NSString stringWithFormat:@"%.2f",motion.rotationRate.z]; }]; } - (IBAction)motionSwitchHandler:(id)sender { UISwitch *motionSwitch = (UISwitch *)sender; if(motionSwitch.on) { [self controlHardware]; } else { [self.motionManager stopDeviceMotionUpdates]; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end
P.s. 只有2010年后的設備支持陀螺儀。要檢查設備是否提供了這種支持,可使用CMMotionManager的布爾屬性gyroAvailable,如果其值為YES,則表明當前設備支持陀螺儀。