獲取設備的移動事件
當我們移動铛绰,晃動岩喷,或者傾斜手機的時候视事,這些動作都會被設備的硬件捕獲匈勋。其實每一個動都都會在 X, Y, Z 三個方向上產(chǎn)生速度上的變化礼旅。根據(jù)不同的變化我們可以檢測出來設備的朝向和移動。要檢測設備的朝向和移動洽洁,我們有三種方法:
- 利用 UIDevice痘系,我們可以獲取設備的大致方向,例如屏幕朝上還是朝下饿自。
- 利用 UIKit 中的 UIEvent汰翠,我們可以獲取設備的晃動
- 利用 Core Motion 來精確的獲取設備在 X Y Z 三個軸上的變化。這個是最精確的昭雌。
使用 UIDevice 來獲取設備的方向
首先我們應該告訴 UIDevice 來檢測設備方向變化复唤,然后我們就可以在 UIDevice 中的 orientation 屬性得到設備方向的變化。
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down
} __TVOS_PROHIBITED;
如果你想檢測設備的方向變化烛卧,那么你應該監(jiān)聽 UIDeviceOrientationDidChangeNotification 通知佛纫,系統(tǒng)會在設備方向發(fā)生變化的時候發(fā)出該通知妓局。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
NSLog(@"%ld",[UIDevice currentDevice].orientation);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cheange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)cheange:(NSNotification *)notification {
UIDevice *device = notification.object;
NSLog(@"%ld", device.orientation);
}
為了延長電池的使用壽命,我們應該在不使用的時候告訴 UIDevice 來關(guān)閉加速計呈宇。
- (void)viewDidDisappear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}
使用 UIEvent 來獲取設備搖晃事件
當用戶晃動設備的時候好爬,iOS 系統(tǒng)會評估加速計的數(shù)據(jù),當這些數(shù)據(jù)特征符合搖晃的時候甥啄,系統(tǒng)會就會認為用戶在搖晃設備存炮,然后會創(chuàng)建一個 UIEvent 對象來發(fā)送個宿主 APP,讓其處理蜈漓。
移動事件和點擊事件很像穆桂。系統(tǒng)會告訴 APP 一個運動的開始和結(jié)束,但并不是每一個動作發(fā)生時都會這么做融虽。
檢測晃動超級簡單享完。晃動這個是可以在模擬器中試驗的衣形。
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"shake");
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(@"shake");
}
設置并檢查硬件是否支持設備移動事件
如果你需要獲取加速計數(shù)據(jù)驼侠,你應該在 Info.plist 中添加相應的鍵值對,如果在一個沒有陀螺儀的設備上打開需要陀螺儀的應用谆吴,那么將會崩潰倒源。App Store 也會根據(jù)你所需要的硬件能力來提醒用戶來是否要下載一個打不開的 APP。
如果你只是獲取設備的大致方向句狼,可以不用添加
使用 Core Motion 來捕獲設備的移動
Core Motion 框架提供加速計和陀螺儀的原始數(shù)據(jù)給 APP 處理笋熬,它使用特別的算法來處理收集到的數(shù)據(jù),精確度更高腻菇,而且是在自己的線程中處理胳螟。
Core Motion 和 UIKit 是不同的,它和 UIEvent 無關(guān)筹吐,也不隨著響應鏈傳遞糖耸,它只是直接的交付數(shù)據(jù)。
Core Motion 所產(chǎn)生的數(shù)據(jù)有三類
- CMAltimeter 高度計
- CMPedometer 計步器
- CMAccelerometerData 捕獲每一個軸上的加速度
- CMGyroData 捕獲三個軸上的旋轉(zhuǎn)率
*CMDeviceMotion 包含了幾個不同的測量丘薛,數(shù)據(jù)更加精確
一般來說嘉竟,一個 APP 只需要創(chuàng)建一個 CMMotionManager 對象,設置其更新間隔洋侨,請求數(shù)據(jù)舍扰,然后處理,每一個數(shù)據(jù)類都是 CMLogItem 的子類希坚,所有的數(shù)據(jù)都會被打上時間的標記并且可以輸出到日志文件中边苹。
CMMotionManager 有兩種獲取數(shù)據(jù)的方法,pull
push
-
pull
APP 主動去請求數(shù)據(jù)更新 -
push
APP 調(diào)用加速計后裁僧,實現(xiàn)回調(diào)的block
个束,當加速計有數(shù)據(jù)更新的時候慕购,就回去調(diào)用這個block
,然后在其中處理相應的數(shù)據(jù)即可
一般來說使用pull
更為方便播急,因為它的代碼更少脓钾,用到了再去取相應的數(shù)據(jù)售睹。
不用的時候桩警,記得關(guān)閉傳感器,這樣更加省電昌妹。
選擇一個合適的更新時間
當你使用 Core Motion 的時候捶枢,你需要指定一個更新間隔。這個時間應該是你的 APP 所能接收的最大間隔飞崖,因為這樣烂叔,可以提升電池的續(xù)航。下邊這個表格提供了常用的更新頻率固歪,很少有 APP 需要每秒鐘獲取 100 次的加速計數(shù)據(jù)蒜鸡。
事件頻率(HZ) | 用途 |
---|---|
10 - 20 | 確定設備的當前朝向 |
30 - 60 | 游戲或者以設備加速度作為實時的用戶輸入數(shù)據(jù) |
70 - 100 | 適用于 APP 需要高頻率的運動事件,例如牢裳,可以根據(jù)這個來判斷用戶快速點擊和搖晃設備 |
使用高度計 CMAltimeter
在手機中逢防,一般使用的是氣壓高度計,根據(jù)當前位置的大氣壓蒲讯,獲取當前位置的高度忘朝。具體公式為
z=cT log(P0/P)
c 為常數(shù),T 為絕對溫度判帮,P 為在高度 z 處的氣壓局嘁,P0 為在海平面處的大氣壓,常數(shù) c 與重力加速度和空氣的摩爾質(zhì)量相關(guān)
我們知道晦墙,當高度上升12米悦昵,氣壓計中的汞柱下降1mm,標準大氣壓下是760mm的汞柱晌畅,一百帕相當于 3/4 mm的汞柱 我們可以得到簡單的計算公式
當前高度 =(760 - 當前大氣壓 * 3 / 4)*12
我們使用 + isRelativeAltitudeAvailable;
來判斷高度計是否可用但指,使用 - startRelativeAltitudeUpdatesToQueue: withHandler:
來獲得當前高度,在這個回調(diào) block 中踩麦,我們會得到一個 CMAltitudeData
對象枚赡,該對象有兩個屬性,一個是當前位置的大氣壓谓谦,單位是 kPa贫橙,一個是當前位置,相對于起始測量位置的高度反粥。例如卢肃,當你剛開始測量的時候疲迂,該值為 0 ,當你拿著設備運動的時候莫湘,這個值會返回你當前位置尤蒿,相對于起始位置的高度,具體高度幅垮,可以根據(jù)當前位置的壓強計算出來腰池,但如果遇到氣壓波動比較大的位置,高度誤差忙芒,還是相當大的示弓。
使用計步器 CMPedometer
當人攜帶者設備走動的時候,會產(chǎn)生一定的頻率呵萨,不同的運動產(chǎn)生的頻率是不一樣的奏属,或走,或坐潮峦,或跑囱皿,根據(jù)不同的頻率濾波,識別忱嘹,最終可以大致確定設備持有者的運動狀態(tài)嘱腥。
在 iOS 8 以后,我們使用 CMPedometer 德谅,而在 iOS 7 - iOS 8 我們使用 CMStepCounter 來獲取數(shù)據(jù)
在 CMPedometer爹橱, 我們可以獲取一下數(shù)據(jù)
- 步數(shù)
- 距離
- 樓層變化
- 速度
- 節(jié)奏
- 運動的開始暫停
當你開始調(diào)用的時候,即使應用被壓到后臺窄做,系統(tǒng)還是可以記錄步數(shù)愧驱,當你再次返回的時候,系統(tǒng)會把這段時間積累的步數(shù)全部返回給你椭盏。我們可以使用系統(tǒng)提供的 API 來查詢某一段時間的運動數(shù)據(jù)组砚,但是,系統(tǒng)最多只為我們保存 7 天掏颊。
//從某個時間開始記錄步數(shù)
-startPedometerUpdatesFromDate: withHandler
//查詢某個時間的段的數(shù)據(jù)
-queryPedometerDataFromDate:toDate:withHandler
//停止記錄步數(shù)
-stopPedometerUpdates
使用 Core Motion 獲取加速計數(shù)據(jù)
加速計測量了三個軸的速度改變糟红。在 Core Motion 中,每一個運動都被捕獲進一個 CMAccelerometerData 對象乌叶,而這個對象包含了一個 CMAcceleration 結(jié)構(gòu)體盆偿。
首先創(chuàng)建一個 CMMotionManager 對象
- 使用 pull 方式獲取數(shù)據(jù),當調(diào)用
startAccelerometerUpdates
方法后准浴,Core Motion 會用最新的測量數(shù)據(jù)更新 CMMotionManager 類的 accelerometerData 屬性事扭,之后你可以在一個循環(huán)中或者定時器中獲取 accelerometerData 來判斷設備的運動。 - 使用 push 方式來獲取數(shù)據(jù)乐横,當調(diào)用
startAccelerometerUpdatesToQueue:withHandler:
當數(shù)據(jù)更新的時候求橄,就會自動調(diào)用 handler今野,在這個 block 中,你可以處理 accelerometerData
self.mManager = [[CMMotionManager alloc] init];
if (self.mManager.accelerometerAvailable) {
NSLog(@"available");
}
self.mManager.accelerometerUpdateInterval = 1;
// pull
[self.mManager startAccelerometerUpdates];
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%f", self.mManager.accelerometerData.acceleration.x);
}];
// push
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"accelerometer";
[self.mManager startAccelerometerUpdatesToQueue:queue withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) {
NSLog(@"x : %f", accelerometerData.acceleration.x);
NSLog(@"y : %f", accelerometerData.acceleration.y);
NSLog(@"z : %f", accelerometerData.acceleration.z);
}];
// stop
- (void)stopUpdates {
if ([self.mManager isAccelerometerActive] == YES) {
[self.mManager stopAccelerometerUpdates];
}
}
處理陀螺儀數(shù)據(jù)
角速度傳感器罐农,測量物體的偏轉(zhuǎn)条霜,角速度。在手機上涵亏,單憑加速器是無法構(gòu)建完整的 3D 動作宰睡,加上陀螺儀,可以讓我們清晰感測到手機的旋轉(zhuǎn)溯乒。 陀螺儀測量設備繞著 X Y Z 三個軸的自傳速率
當你請求陀螺儀數(shù)據(jù)更新的時候夹厌,Core Motion 返回的數(shù)據(jù)是有誤差的。它會返回一個 CMGyroData 對象裆悄,該對象中有一個 rotationRate 屬性,它是一個 CMRotationRate 結(jié)構(gòu)體臂聋,里邊包含了三個方向上的自傳速率光稼,單位是弧度每秒。 用 CMGyroData 是有誤差的孩等,如果想獲取更為精確是自傳速率艾君,可以使用 CMDeviceMotion 。
其更新數(shù)據(jù)方式也是有兩種肄方,分為 startGyroUpdates
pull 和 startGyroUpdatesToQueue:withHandler:
push冰垄,具體方式和加速計獲取方式一樣,取的數(shù)據(jù)在 CMMotionManager 的 gyroData 屬性中权她,同樣虹茶,你也需要設置更新的時間間隔。
代碼就不貼了隅要,和上邊大同小異蝴罪。
旋轉(zhuǎn)方向遵循右手定則,右手握拳步清,大拇指指向某一個軸要门,如果旋轉(zhuǎn)的方向和四個手指的方向一樣的話,即為正廓啊,否則就為負
處理設備數(shù)據(jù)
如果設備上既有加速計又有陀螺儀的話欢搜,Core Motion 提供了一個 device-motion 的服務用來處理兩個傳感器的原始數(shù)據(jù)。使用這個谴轮,可以得到最精確的數(shù)據(jù)炒瘟。
你可以訪問這個 CMDeviceMotion 對象的 attitude 屬性,每一個 CMAttitude 對象包含了下邊三種數(shù)據(jù)
- 一個四元組
- 一個旋轉(zhuǎn)率矩陣
- 歐拉角
調(diào)用方式和上邊測量加速度發(fā)方式類似书聚,代碼就不貼了唧领。
關(guān)于歐拉角
描述一個物體的旋轉(zhuǎn)藻雌,往往是最難的。歐拉角是表達旋轉(zhuǎn)的最簡單的一種方式斩个,形式上它是一個三維向量胯杭,其值分別代表物體繞坐標系三個軸(x,y,z軸)的旋轉(zhuǎn)角度。
但是受啥,這么描述的話做个,會有一個問題,那就是萬向鎖滚局。
歐拉旋轉(zhuǎn)可以靠這種順序讓一個物體指到任何一個想要的方向居暖,但如果在旋轉(zhuǎn)中不幸讓某些坐標軸重合了就會發(fā)生萬向節(jié)鎖,這時就會丟失一個方向上的旋轉(zhuǎn)能力藤肢,也就是說在這種狀態(tài)下我們無論怎么旋轉(zhuǎn)(當然還是要原先的順序)都不可能得到某些想要的旋轉(zhuǎn)效果太闺。具體可以點這里來了解。
由于大多數(shù)都是物理知識嘁圈,我這個農(nóng)民工省骂,了解的還不是很詳細,數(shù)據(jù)我們是得到了最住,具體怎么應用到現(xiàn)實中钞澳,還要慢慢摸索。