iOS實現(xiàn)模擬定位功能的示例代碼
前言
App中越來越多的功能依賴用戶實際的位置,例如基于用戶位置提供推薦數(shù)據(jù)、基于定位判斷某些功能是否可用,但是在開發(fā)調試中XCode卻沒有提供自定義的模擬定位的功能,所以本文主要的目的是現(xiàn)實一個可以在開發(fā)調試過程中隨時模擬定位的功能。
思路
我們在iOS的app開發(fā)中通常采用的是CLLocationManager來獲取用戶當前的位置,當然也可以采用MKMapView的showUserLocation來獲取用戶的位置,所以我們分別針對這兩種情況分析。
CLLocationManager
采用CLLocationManager獲取定位時,是根據(jù)CLLocationManagerDelegate中- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations的回調來獲取到定位的。我們只需要在系統(tǒng)回調這個方法傳遞給業(yè)務代碼的中間,插入一部分代碼,來修改locations參數(shù)。原本的邏輯為系統(tǒng)回調->業(yè)務代碼,現(xiàn)在變?yōu)橄到y(tǒng)回調->模擬定位模塊->業(yè)務代碼,就實現(xiàn)了無侵入式的實現(xiàn)模擬定位功能。為了實現(xiàn)這個邏輯,可以有以下幾個思路。
1、 Runtime swizzle
因為業(yè)務代碼是根據(jù)- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations方法來接受回調的,所以可以采用Runtime swizzle這個方法,來實現(xiàn)模擬定位的功能,但是我們中間組件是不知道業(yè)務代碼中具體是哪個類,所以無法具體指定runtime swizzle哪個類,所以只能遍歷所有的類,判斷當前類的方法列表中是否有l(wèi)ocationManager:didUpdateLocations:這個方法,如果存在則swizzle。
- 優(yōu)點:便于理解。
- 缺點:需要遍歷所有的類和類的方法列表。
2、中間代理對象
這種思路是Swizzle了CLLocationManager的setDelegate:方法,當調用setDelegate時,將真實的delegate object保存下來,再將我們定義的中間代理類swizzle delegate對象設置為CLLocationManager的delegate,這樣當系統(tǒng)回調CLLocationManagerDelegate,會先回調到中間代理類swizzle delegate中,再由swizzle delegate將事件傳遞到真實的delegate object。
- 優(yōu)點:相對于第一種方法,不需要遍歷類和類的方法列表,只需swizzle CLLocationManager中的setDelegate:方法即可。
- 缺點:在中間代理類swizzle delegate中需要實現(xiàn)全部的CLLocationManagerDelegate方法,如果后續(xù)增加代理方法,仍需要修改這個類。
3、采用NSProxy實現(xiàn)中間代理對象
Objective-C中有2個基類,常用的就是NSObject,另一個就是NSProxy,NSProxy主要用于消息轉發(fā)處理,所以采用NSProxy我們可以更好的處理方法二中的缺點。
3.1創(chuàng)建一個新的類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
接著就來處理消息轉發(fā)的邏輯,首先我們要知道我們想要的是什么效果,系統(tǒng)回調給MockLocationProxy,MockLocationProxy只處理locationManager:didUpdateLocations:,其他的消息都仍然交給原target。
所以我們在MockLocationProxy.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
當消息發(fā)送給MockLocationProxy時,判斷當前方法是否是locationManager:didUpdateLocations:,如果是,則MockLocationProxy響應事件,否則直接傳遞給原本的target。到此已經(jīng)可以隨時處理模擬定位。你只需要在模擬定位的代碼做一些處理,就可以隨時修改定位。
One more.
上述方法雖然可以模擬定位,但是每次修改模擬值都需重新build,那么有沒有辦法在運行時隨時修改這個值呢?
LLDebugTool
當然可以,你只需要在你的項目中集成LLDebugTool,調用其中的Location模塊,LLDebugTool提供了一個UI來隨時修改這個模擬值,讓你在調試時,隨時模擬定位,LLDebugTool仍提供了很多其他的功能,如果你只需要模擬定位的功能,則只需要集成LLDebugTool/Location這個subspec就可以了。
后記
前言說過,定位除了CLLocationManager之外,MKMapView的showUserLocation也可以獲取定位信息,那么如何解決這個問題呢? 你可以在LLDebugTool/Location中查看答案。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持我們。
您可能感興趣的文章
- 01-11iOS常用算法之兩個有序數(shù)組合并(要求時間復雜度為0(n))
- 01-11iOS 彈幕功能的實現(xiàn)思路圖解
- 01-11iOS調試Block引用對象無法被釋放的小技巧分享
- 01-11iOS動態(tài)更換Icon的全過程記錄
- 01-11iOS實現(xiàn)文本分頁的方法示例
- 01-11iOS常見宏理解及使用方法
- 01-11iOs遷至WKWebView跨過的一些坑
- 01-11iOS模擬中獎名單循環(huán)滾動效果
- 01-11Python一鍵查找iOS項目中未使用的圖片、音頻、視頻資源
- 01-11iOS中如何獲取某個視圖的截圖詳析


閱讀排行
本欄相關
- 01-11UILabel顯示定時器文本跳動問題的解決
- 01-11iOS常用算法之兩個有序數(shù)組合并(要
- 01-11iOS 彈幕功能的實現(xiàn)思路圖解
- 01-11詳解MacOs免密登錄CentOs操作步驟
- 01-11iOS動態(tài)更換Icon的全過程記錄
- 01-11iOS調試Block引用對象無法被釋放的小技
- 01-11iOS常見宏理解及使用方法
- 01-11iOS實現(xiàn)文本分頁的方法示例
- 01-11iOs遷至WKWebView跨過的一些坑
- 01-11iOS模擬中獎名單循環(huán)滾動效果
隨機閱讀
- 01-10C#中split用法實例總結
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-10delphi制作wav文件的方法
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11ajax實現(xiàn)頁面的局部加載