目錄
- 背景介紹
- 探索程序
- 其他 APP 有沒有類似功能
- 系統提供的搖一搖回呼能否滿足
- 其他方法能否實作
- 利用 CoreMotion 框架,監聽加速計原始資料
- 通過加速計監聽搖一搖
- 控制器相關邏輯和代碼
- APP 申請后臺運行權限后,能否監聽到搖一搖
- 利用 CoreMotion 框架,監聽加速計原始資料
- 多 APP 都實作此功能時,搖一搖是何效果
- 后臺定位權限 + 系統搖一搖,是否可行?
- 文章小結
- 參考文章
- Demo 鏈接
背景介紹
一般情況下,出于省電、權限、合理性等因素考慮,給人的感覺是很多奇怪的需求安卓可以實作,但是iOS就無法實作!今天要介紹的需求也有這種感覺,就是“當 APP 處于后臺或鎖屏狀態時,依舊可以監聽到搖一搖,進而觸發某些功能,比如:語音播報”,
在產品經理提出此需求的一瞬間,仿佛周邊的空氣都凝固了,我也猶如五雷轟頂,愣在原地無法動彈,不由心想:“蘋果爸爸怎么可能允許開發者實作這種功能!這得多費電啊!要是所有 APP 都這么做了,那還了得!” 與此同時,之前網上瘋傳、遠近聞名的的需求--“做一個會根據手機殼顏色而改變主題顏色的APP”,清晰地浮現在腦海中,頓時一萬只xx??從心中奔騰而過,此時,產品經理解釋到,這是咱們好多視力障礙用戶提的需求,他們經常鎖屏或把 APP 退到后臺,且因為視力不佳原因,導致重新找到 APP 并切到前臺的操作很是麻煩,所以十分希望我們能實作這個功能,
在短暫的心理活動后,秉著“客戶第一,產品????”的原則,于是回復說:“這功能太少見了,我先在網上看看吧,要是有其他 APP 有類似的功能,麻煩跟我說我參考一下,”然后,就祭出了程式員利器--Google,輸入“iOS 后臺 搖一搖”,只搜索出來的一個思路:利用 CoreMotion 框架,監聽加速計原始資料,然后在 APP 退到后臺后,可以實作監聽搖一搖的效果,然而,并沒有完整的代碼或 demo ,頓時,Talk is cheap, show me the code!這句經典臺詞突然地出現在腦海中!也看到有人評論說 CoreMotion 的確可以實作跟系統搖一搖類似的效果,但是退到后臺或鎖屏后,沒辦法監聽到搖一搖事件,
看到這條評論時,我不禁開始懷疑此功能是否真的可以被實作,
玩歸玩,鬧歸鬧,開始 code,不開玩笑,
接下來,開始自己的探索之旅,
本文 demo 鏈接為 OCDailyTests/BackgroundShakeTest,可自行下載,方便運行和驗證,
探索程序
其他 APP 有沒有類似功能
經過一番 Google,終于找到一款 APP 有類似功能::酷狗音樂 APP,對,就是那個在 PC 端一打開就會大喊 Hello KuGou!的音樂軟體對應的 APP,萬萬沒想到,手機 APP 也是這樣,一句Hello KuGou!把我嚇一跳,按如下步驟,在設定里打開此功能后,后臺或鎖屏時,搖一搖手機,可實作切歌的效果,



既然的確有 APP 實作了此功能,那就踏踏實實地探索它可能是怎么實作的吧,
系統提供的搖一搖回呼能否滿足
系統搖一搖回呼方法:
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event{
NSLog(@"%s", __FUNCTION__);
}
經測驗,此方法只有在 APP 處于前臺時,才會被回呼,APP 處于后臺或鎖屏時,此方法不會回呼,故初步判定此方法不能滿足需求,
其他方法能否實作
此時,還是先根據網上各路大神提供的思路進行嘗試,即利用 CoreMotion 框架,監聽加速計原始資料,然后在 APP 退到后臺后,實作監聽搖一搖的效果,
好,我們先利用 CoreMotion 框架,監聽加速計原始資料,實作類似系統搖一搖回呼的效果,
利用 CoreMotion 框架,監聽加速計原始資料
通過加速計監聽搖一搖
因加速計回呼比較頻繁,因此比較占用資源,故把此功能設計為單例,
-
快速實作單例效果
//具體實作詳見 demo 中檔案 #import "HMSingleton.h" @interface MYAccelerometerTool : NSObject HMSingleton_h(MYAccelerometerTool); @end @implementation MYAccelerometerTool HMSingleton_m(MYAccelerometerTool); @end -
宣告和懶加載運動管理員屬性
@property(nonatomic, strong) CMMotionManager *gMotionMnger; - (CMMotionManager *)gMotionMnger{ if (nil == _gMotionMnger) { CMMotionManager *lMnger = [[CMMotionManager alloc] init]; lMnger.accelerometerUpdateInterval = 0.1; [lMnger startAccelerometerUpdates]; _gMotionMnger = lMnger; } return _gMotionMnger; } -
宣告和實作時間戳屬性,用于實作節流效果(為防止頻繁回呼,每次檢測成功后,停止搖動 1s 后才繼續回應下次搖一搖,)
@property(nonatomic, strong) NSDate *gDateLastShakeSuc; - (NSDate *)gDateLastShakeSuc{ if (nil == _gDateLastShakeSuc) { _gDateLastShakeSuc = [NSDate distantPast]; } return _gDateLastShakeSuc; } -
開始監聽搖一搖動作
- (BOOL)startMonitorShake{ if (NO == self.gMotionMnger.isAccelerometerAvailable) { return NO; } //監聽中,直接回傳YES if (self.gMotionMnger.isAccelerometerActive) { return YES; } [self.gMotionMnger startAccelerometerUpdatesToQueue:[NSOperationQueue new] withHandler:^(CMAccelerometerData * _Nullable accelerometerData, NSError * _Nullable error) { CMAcceleration acceleration = accelerometerData.acceleration; //綜合x、y兩個方向的加速度(z方向速度無意義,用的話,走路上下抖手機時會誤觸發,系統搖一搖也不會被z軸加速度觸發) //當綜合加速度大于2.3時,就激活效果(資料越小,用戶搖動的動作就越小,越容易激活) double accelerameter = sqrt( pow( acceleration.x , 2 ) + pow( acceleration.y , 2 )); if (accelerameter > 2.3) { //節流效果:距離上次搖一搖成功事件,間隔時間小于1s時,認為無效 NSDate *lCrtDate = [NSDate date]; if ([lCrtDate timeIntervalSinceDate:self.gDateLastShakeSuc] < 1) { self.gDateLastShakeSuc = lCrtDate; return ; } self.gDateLastShakeSuc = lCrtDate; [[NSNotificationCenter defaultCenter] postNotificationName:KNTFY_SHAKE_SUCCESS object:nil]; } }]; return YES; } -
為了代碼的對稱美和可能的相關業務,實作停止監聽搖一搖方法
- (void)stopMonitorShake{ [self.gMotionMnger stopAccelerometerUpdates]; self.gMotionMnger = nil; self.gDateLastShakeSuc = nil; }
控制器相關邏輯和代碼
-
開始監聽搖一搖
BOOL lRes = [[MYAccelerometerTool sharedMYAccelerometerTool] startMonitorShake]; NSLog(@"lRes:%d", lRes); NSAssert(lRes, @"開始監聽搖一搖失敗"); -
監聽搖一搖成功的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nmShakeSuccess:) name:KNTFY_SHAKE_SUCCESS object:nil]; //在搖一搖的同時,通過觀察此方法是否有log,可以判斷是否有監聽到, - (void)nmShakeSuccess:(NSNotification *)ntfy{ NSLog(@"%s", __FUNCTION__); } -
dealloc方法中取消監聽
- (void)dealloc{ [[NSNotificationCenter defaultCenter] removeObserver:self]; }運行 demo 工程,測驗可知,通過上述方法,的確可以在 APP 處于前臺時,實作監聽搖一搖動作的效果,可是,當把 APP 退到后臺或鎖屏時,nmShakeSuccess 方法不再有 log,即:APP 處于后臺時,通過監聽加速計的方法,默認也無法在 APP 處于后臺或鎖屏時實作監聽效果,這也印證了上文提到的那個評論者的疑問,
可是
Hello KuGou!明明實作了后臺或鎖屏時搖一搖的效果啊!難道是需要額外的配置?聯想 iOS 處于后臺時,默認會把 APP 的服務給掛起(suspended),只有當 APP 通過某種方式(后臺定位/播放音樂/藍牙掃描等)具有后臺運行權限時,才可以一直保活,可猜想,也許賦予 APP 具有后臺運行的權限后,就可以實作想要的功能了,于是,開始進行驗證如下,
APP 申請后臺運行權限后,能否監聽到搖一搖
因為作業中很多 APP 具有后臺定位權限和相關功能,所以本文通過為 APP 申請后臺定位權限來驗證,
APP 申請后臺定位權限
-
plist 檔案中增加”定位請求描述資訊“
<key>NSLocationAlwaysUsageDescription</key> <string>我們需要根據您的定位提供周邊搜索和導航服務</string> <key>NSLocationWhenInUseUsageDescription</key> <string>我們需要根據您的定位提供周邊搜索和導航服務</string>增加”后臺定位權限“
<key>UIBackgroundModes</key> <array> <string>location</string> </array> -
宣告定位管理員屬性
@property(nonatomic, strong) CLLocationManager *gMnger; -
懶加載定位管理員,請求定位權限、允許后臺位置更新
- (CLLocationManager *)gMnger{ if (nil == _gMnger) { _gMnger = [[CLLocationManager alloc] init]; _gMnger.delegate = self; _gMnger.allowsBackgroundLocationUpdates = YES; [_gMnger requestWhenInUseAuthorization]; } return _gMnger; } -
代理 3 步走(用于驗證后臺定位是否生效)
-
遵守代理協議
@interface ViewController ()<CLLocationManagerDelegate> -
指定代理物件
_gMnger.delegate = self; -
實作代理方法
#pragma mark - delegate - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations{ NSLog(@"%s", __FUNCTION__); } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{ NSLog(@"%s", __FUNCTION__); }
-
-
APP 后臺或鎖屏后,測驗能否成功監聽搖一搖
運行 demo 工程,經測驗,把 APP 退到后臺或鎖屏,或即退到后臺又鎖屏,都能夠檢測到搖一搖事件,
多 APP 都實作此功能時,搖一搖是何效果
這里用 demo APP 和酷狗音樂 APP 進行測驗,
- 同時打開這兩個 APP,其中酷狗音樂 APP 打開后臺搖一搖切歌的功能,
- 酷狗音樂 APP 開始放歌,退到后臺,
- demo APP 打開后,退到后臺,
- 搖一搖,查看效果:
- 當搖動的力度不是很大時,demo APP 回呼方法會被觸發;
- 當搖動的力度很大時,demo APP 回呼方法和酷狗 APP 切歌會同時被觸發;
- 由此可見,如果多個 APP 同時實作了此功能時,那么后臺或鎖屏搖一搖時,只要滿足了某個 APP 實作的加速計相關判定條件,就可以同時觸發多個 APP 對應的效果,
后臺定位權限 + 系統搖一搖,是否可行?
經測驗,還是不行,果然,系統搖一搖還是比較受限的,只能在前臺回呼,
文章小結
想要實作”iOS后臺鎖屏監聽搖一搖“功能,
首次,必須滿足一個硬性條件:APP 具有某種后臺運行的權限,
其次,技術實作上必須使用CoreMotion框架,通過監聽加速計回呼自己實作對搖一搖事件的監聽判定,
最后,可通過增加時間屬性,實作對搖一搖事件監聽時的節流效果,防止持續搖動時,太過頻繁的事件回呼,
此外,多 APP 都實作此功能時,搖一搖的效果是:只要搖動力度很大,加速計資料滿足 APP 實作的搖一搖判定條件,就可以同時觸發多個 APP 各自對應的效果,
因此,如果不是 APP 特別需要此功能,盡量不要這樣實作,畢竟,比較占用系統資源,而且太多 APP 同時實作時,可能會出現效果上的相互干擾,不過,如果合理利用此功能,卻可以為特殊用戶群體提供極大的便利!
通過探索,滿足了視力障礙用戶的迫切需求,還是蠻有成就感的!
偷偷的告訴大家,寫到這里時,產品經理還沒告訴我他所知道的哪個 APP 實作了這個功能,可能他太忙,給忘記了吧......
參考文章
iOS應用退出到后臺后怎樣監聽搖晃事件
Demo 鏈接
OCDailyTests/BackgroundShakeTest
最后,感謝“技術創作101訓練營”!通過參加訓練營,讓我對寫作有了更深入的認識和更高的心里覺悟,
本文由博客群發一文多發等運營工具平臺 OpenWrite 發布
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/164815.html
標籤:iOS
