iOS實(shí)現(xiàn)模擬定位功能的示例代碼
前言
App中越來越多的功能依賴用戶實(shí)際的位置,例如基于用戶位置提供推薦數(shù)據(jù)、基于定位判斷某些功能是否可用,但是在開發(fā)調(diào)試中XCode卻沒有提供自定義的模擬定位的功能,所以本文主要的目的是現(xiàn)實(shí)一個(gè)可以在開發(fā)調(diào)試過程中隨時(shí)模擬定位的功能。
思路
我們?cè)趇OS的app開發(fā)中通常采用的是CLLocationManager來獲取用戶當(dāng)前的位置,當(dāng)然也可以采用MKMapView的showUserLocation來獲取用戶的位置,所以我們分別針對(duì)這兩種情況分析。
CLLocationManager
采用CLLocationManager獲取定位時(shí),是根據(jù)CLLocationManagerDelegate中- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations的回調(diào)來獲取到定位的。我們只需要在系統(tǒng)回調(diào)這個(gè)方法傳遞給業(yè)務(wù)代碼的中間,插入一部分代碼,來修改locations參數(shù)。原本的邏輯為系統(tǒng)回調(diào)->業(yè)務(wù)代碼,現(xiàn)在變?yōu)橄到y(tǒng)回調(diào)->模擬定位模塊->業(yè)務(wù)代碼,就實(shí)現(xiàn)了無(wú)侵入式的實(shí)現(xiàn)模擬定位功能。為了實(shí)現(xiàn)這個(gè)邏輯,可以有以下幾個(gè)思路。
1、 Runtime swizzle
因?yàn)闃I(yè)務(wù)代碼是根據(jù)- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations方法來接受回調(diào)的,所以可以采用Runtime swizzle這個(gè)方法,來實(shí)現(xiàn)模擬定位的功能,但是我們中間組件是不知道業(yè)務(wù)代碼中具體是哪個(gè)類,所以無(wú)法具體指定runtime swizzle哪個(gè)類,所以只能遍歷所有的類,判斷當(dāng)前類的方法列表中是否有l(wèi)ocationManager:didUpdateLocations:這個(gè)方法,如果存在則swizzle。
- 優(yōu)點(diǎn):便于理解。
- 缺點(diǎn):需要遍歷所有的類和類的方法列表。
2、中間代理對(duì)象
這種思路是Swizzle了CLLocationManager的setDelegate:方法,當(dāng)調(diào)用setDelegate時(shí),將真實(shí)的delegate object保存下來,再將我們定義的中間代理類swizzle delegate對(duì)象設(shè)置為CLLocationManager的delegate,這樣當(dāng)系統(tǒng)回調(diào)CLLocationManagerDelegate,會(huì)先回調(diào)到中間代理類swizzle delegate中,再由swizzle delegate將事件傳遞到真實(shí)的delegate object。
- 優(yōu)點(diǎn):相對(duì)于第一種方法,不需要遍歷類和類的方法列表,只需swizzle CLLocationManager中的setDelegate:方法即可。
- 缺點(diǎn):在中間代理類swizzle delegate中需要實(shí)現(xiàn)全部的CLLocationManagerDelegate方法,如果后續(xù)增加代理方法,仍需要修改這個(gè)類。
3、采用NSProxy實(shí)現(xiàn)中間代理對(duì)象
Objective-C中有2個(gè)基類,常用的就是NSObject,另一個(gè)就是NSProxy,NSProxy主要用于消息轉(zhuǎn)發(fā)處理,所以采用NSProxy我們可以更好的處理方法二中的缺點(diǎn)。
3.1創(chuàng)建一個(gè)新的類MockLocationProxy,集成自NSProxy。
// MockLocationProxy.h #import <CoreLocation/CoreLocation.h> @interface MockLocationProxy : NSProxy @property (nonatomic, weak, readonly, nullable) id <CLLocationManagerDelegate> target; - (instancetype)initWithTarget:(id <CLLocationManagerDelegate>)target; @end
// MockLocationProxy.m #import "MockLocationProxy.h" @implementation MockLocationProxy - (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target { _target = target; return self; } @end
接著就來處理消息轉(zhuǎn)發(fā)的邏輯,首先我們要知道我們想要的是什么效果,系統(tǒng)回調(diào)給MockLocationProxy,MockLocationProxy只處理locationManager:didUpdateLocations:,其他的消息都仍然交給原target。
所以我們?cè)贛ockLocationProxy.m中添加以下方法:
// MockLocationProxy.m @implementation MockLocationProxy - (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target { _target = target; return self; } - (BOOL)respondsToSelector:(SEL)aSelector { if (aSelector == @selector(locationManager:didUpdateLocations:)) { return YES; } return [self.target respondsToSelector:aSelector]; } - (void)forwardInvocation:(NSInvocation *)invocation { SEL sel = invocation.selector; if ([self.target respondsToSelector:sel]) { [invocation invokeWithTarget:self.target]; } } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; } #pragma mark - CLLocationManagerDelegate - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations { if ([self.target respondsToSelector:_cmd]) { // 模擬定位代碼 CLLocation *mockLocation = [[CLLocation alloc] initWithLatitude:39.908722 longitude:116.397499]; locations = @[mockLocation]; [self.target locationManager:manager didUpdateLocations:locations]; } } @end
當(dāng)消息發(fā)送給MockLocationProxy時(shí),判斷當(dāng)前方法是否是locationManager:didUpdateLocations:,如果是,則MockLocationProxy響應(yīng)事件,否則直接傳遞給原本的target。到此已經(jīng)可以隨時(shí)處理模擬定位。你只需要在模擬定位的代碼做一些處理,就可以隨時(shí)修改定位。
One more.
上述方法雖然可以模擬定位,但是每次修改模擬值都需重新build,那么有沒有辦法在運(yùn)行時(shí)隨時(shí)修改這個(gè)值呢?
當(dāng)然可以,你只需要在你的項(xiàng)目中集成LLDebugTool,調(diào)用其中的Location模塊,LLDebugTool提供了一個(gè)UI來隨時(shí)修改這個(gè)模擬值,讓你在調(diào)試時(shí),隨時(shí)模擬定位,LLDebugTool仍提供了很多其他的功能,如果你只需要模擬定位的功能,則只需要集成LLDebugTool/Location這個(gè)subspec就可以了。
后記
前言說過,定位除了CLLocationManager之外,MKMapView的showUserLocation也可以獲取定位信息,那么如何解決這個(gè)問題呢? 你可以在LLDebugTool/Location中查看答案。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS動(dòng)態(tài)調(diào)整UILabel高度的幾種方法
在iOS編程中UILabel是一個(gè)常用的控件,下面這篇文章主要給大家介紹了關(guān)于iOS動(dòng)態(tài)調(diào)整UILabel高度的幾種方法,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12MacOS無(wú)法掛載NFS Operation not permitted錯(cuò)誤解決辦法
這篇文章主要介紹了MacOS無(wú)法掛載NFS Operation not permitted錯(cuò)誤解決辦法的相關(guān)資料2017-02-02iOS開發(fā)避免安全隱患的要點(diǎn)總結(jié)
在本篇文章里小編給各位整理了關(guān)于iOS開發(fā)如何避免安全隱患的知識(shí)點(diǎn)總結(jié),需要的朋友們學(xué)習(xí)下。2019-07-07iOS基于UIScrollView實(shí)現(xiàn)滑動(dòng)引導(dǎo)頁(yè)
這篇文章主要為大家詳細(xì)介紹了iOS基于UIScrollView實(shí)現(xiàn)滑動(dòng)引導(dǎo)頁(yè)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01使用UITextField限制只可輸入中,英文,數(shù)字的方法
在我們?nèi)粘i_發(fā)中經(jīng)常遇到一些情況,要UITextField只能輸入某一種特定的字符.比如大寫A-Z或者小寫a-z,或者漢字.或者數(shù)字.那么該如何實(shí)現(xiàn)呢,下面通過這篇文章來看看吧。2016-09-09iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻
這篇文章主要為大家詳細(xì)介紹了iOS自定義相機(jī)實(shí)現(xiàn)拍照、錄制視頻,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04iOS中實(shí)現(xiàn)圖片自適應(yīng)拉伸效果的方法
圖片拉伸在移動(dòng)開發(fā)中特別常見,比如常用的即時(shí)通訊應(yīng)用中的聊天氣泡就需要根據(jù)文字長(zhǎng)度對(duì)背景圖片進(jìn)行拉伸自適應(yīng)。下面這篇文章主要給大家介紹了iOS中實(shí)現(xiàn)圖片自適應(yīng)拉伸效果的方法,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03iOS動(dòng)畫實(shí)現(xiàn)雨花與櫻花特效
小編今天為大家?guī)硪粓?chǎng)淅淅瀝瀝的夜空之雨和滿天飛舞的櫻花之戀,希望能在炎炎夏日為您帶來一絲清爽的涼意!學(xué)習(xí)iOS動(dòng)畫的小伙伴們可以參考學(xué)習(xí)。2016-08-08