最近在做公司內(nèi)部的一個(gè)項(xiàng)目哀峻。主要需求很簡(jiǎn)單哟绊,就是每隔N分鐘向服務(wù)器發(fā)送設(shè)備的位置,不管此時(shí)App是運(yùn)行在前臺(tái)還是后臺(tái)钠导。
這里總結(jié)一下使用iOS定位服務(wù)的一些關(guān)鍵點(diǎn)和需要注意的地方坐梯。
App 的設(shè)置
- 因?yàn)锳pp需要在后臺(tái)的時(shí)候也能不斷地獲取設(shè)備的位置徽诲。所以要將Capablities里面的BackgroundMode 設(shè)置成Enable。并且勾選其中的Location updates選項(xiàng)吵血。
- 在iOS8以后谎替,需要在info.plist里面添加NSLocationAlwaysUsageDescription或者NSLocationWhenInUseUsageDescription,這兩個(gè)key都是NSString類型。使用哪個(gè)(或者兩者都添加)取決于申請(qǐng)定位的權(quán)限蹋辅,這個(gè)下文會(huì)提到钱贯。這個(gè)所謂的描述就是當(dāng)系統(tǒng)提示用戶App要使用定位的時(shí)候,會(huì)加在系統(tǒng)提示的后面侦另,如圖秩命。
初始化CLLocationManager
使用iOS定位服務(wù)需要引入系統(tǒng)的頭文件并且實(shí)現(xiàn)CLLocationManagerDelegate的代理。
#import <CoreLocation/CoreLocation.h>
先來看一下初始化的代碼:
-(void) createLocationManager{
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
[_locationManager setAllowsBackgroundLocationUpdates:YES];
}
_locationManager.pausesLocationUpdatesAutomatically = NO;
}
iOS8以后褒傅,系統(tǒng)的定位權(quán)限有三種弃锐,對(duì)應(yīng)設(shè)置里面的總是,永不殿托,和App使用期間霹菊。那么根據(jù)我們App的需求,我們需要申請(qǐng)“總是”這種權(quán)限支竹。相應(yīng)地旋廷,我們要在info.plist里面添加的是NSLocationAlwaysUsageDescription鸠按。
if ([_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
并且在iOS9之后,如果需要在后臺(tái)保持定位,除了上文所說的在App的setting和info文件里面設(shè)置以外柳洋,還需要加上下面的代碼:
if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
[_locationManager setAllowsBackgroundLocationUpdates:YES];
}
整個(gè)初始化完成以后待诅,調(diào)用以下API系統(tǒng)就會(huì)開始定位了
[_locationManager startUpdatingLocation];
在代理里面實(shí)現(xiàn)位置更新的代碼
正常來說,完成上面的所有設(shè)置熊镣,就可以使用iOS系統(tǒng)的定位服務(wù)了。
系統(tǒng)會(huì)每秒都調(diào)用
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
這個(gè)代理方法募书,我們所要做的就是在這里處理系統(tǒng)返回回來的位置信息绪囱。
CLLocation這個(gè)類里面包括的一些常用的位置信息有經(jīng)度、緯度莹捡、海拔鬼吵、速度、精確度等等篮赢,根據(jù)項(xiàng)目的需求可以對(duì)其進(jìn)行相應(yīng)的處理齿椅。
到這里,最基礎(chǔ)的部分已經(jīng)完成启泣。接下來會(huì)探討一些別的配置涣脚。
pausesLocationUpdatesAutomatically屬性
貼上一段官網(wǎng)對(duì)這個(gè)屬性的描述:
Allowing the location manager to pause updates can improve battery life on the target device without sacrificing location data. When this property is set to YES, the location manager pauses updates (and powers down the appropriate hardware) at times when the location data is unlikely to change. For example, if the user stops for food while using a navigation app, the location manager might pause updates for a period of time. You can help the determination of when to pause location updates by assigning a value to the activityTypeproperty.
大致的意思就是如果這個(gè)屬性設(shè)置成YES(默認(rèn)的也是YES),那么系統(tǒng)會(huì)檢測(cè)如果設(shè)備有一段時(shí)間沒有移動(dòng)寥茫,就會(huì)自動(dòng)停掉位置更新服務(wù)遣蚀。這里需要注意的是,一旦定位服務(wù)停止了纱耻,只有當(dāng)用戶再次開啟App的時(shí)候定位服務(wù)才會(huì)重新啟動(dòng)芭梯。
這里的一段時(shí)間是系統(tǒng)自動(dòng)判定的,可以通過設(shè)置activityTypeproperty這個(gè)屬性來決定這個(gè)時(shí)間的長(zhǎng)短弄喘。
API的意思是玖喘,類似導(dǎo)航類的App,系統(tǒng)檢驗(yàn)的時(shí)間會(huì)稍長(zhǎng)一點(diǎn),想運(yùn)動(dòng)類的App蘑志,就會(huì)比導(dǎo)航類的短一點(diǎn)累奈。但是具體時(shí)間還是由系統(tǒng)來決定。
DeferredUpdates
默認(rèn)地卖漫,定位服務(wù)的代理會(huì)每秒鐘都更新一次位置费尽,這樣對(duì)電池的消耗量會(huì)特別地大。除了設(shè)置pausesLocationUpdatesAutomatically這個(gè)屬性以外羊始,iOS還提供了DeferredUpdates的機(jī)制旱幼。
官方API文檔:
- (void)allowDeferredLocationUpdatesUntilTraveled:(CLLocationDistance)distance
timeout:(NSTimeInterval)timeout
distance:
The distance (in meters) from the current location that must be travelled before event delivery resumes. To specify an unlimited distance, pass the CLLocationDistanceMaxconstant.
timeout:
The amount of time (in seconds) from the current time that must pass before event delivery resumes. To specify an unlimited amount of time, pass the CLTimeIntervalMax constant.
就是你可以設(shè)置讓系統(tǒng)每隔多遠(yuǎn)或者每隔多長(zhǎng)時(shí)間更新一次位置。注意是“或”的關(guān)系突委,滿足一個(gè)就會(huì)更新柏卤。
使用這個(gè)方法有很多要注意的地方:
- desiredAccuracy必須設(shè)置成kCLLocationAccuracyBest
- distanceFilter必須設(shè)置成kCLErrorDeferredDistanceFiltered
- 必須能夠使用GPS進(jìn)行定位(而不僅僅是移動(dòng)數(shù)據(jù)或者Wi-Fi)
- 非常重要的一點(diǎn)冬三,DeferredUpdates只會(huì)出現(xiàn)在設(shè)備進(jìn)入低耗電量的狀態(tài),App運(yùn)行在前臺(tái)或者設(shè)備連接在Xcode上正在調(diào)試是不會(huì)觸發(fā)的缘缚。(所以不可能在Debug的時(shí)候打印Log來檢驗(yàn)勾笆,要調(diào)試的話,需要寫一些Log存在本地的數(shù)據(jù)庫)
官網(wǎng)的Example:
-(void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
// Add the new locations to the hike
[self.hike addLocations:locations];
// Defer updates until the user hikes a certain distance or a period of time has passed
if (!self.deferringUpdates) {
CLLocationDistance distance = self.hike.goal - self.hike.distance;
NSTimeInterval time = [self.nextUpdate timeIntervalSinceNow];
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:distance timeout:time];
self.deferringUpdates = YES;
} }
-(void)locationManager:(CLLocationManager *)manager
didFinishDeferredUpdatesWithError:(NSError *)error {
// Stop deferring updates
self.deferringUpdates = NO;
// Adjust for the next goal
}
反地理編碼
知道了經(jīng)緯度桥滨,有時(shí)候我們需要獲取這個(gè)經(jīng)緯度對(duì)應(yīng)的詳細(xì)地址信息窝爪,示例如下:
CLGeocoder *revGeo = [[CLGeocoder alloc] init];
[revGeo reverseGeocodeLocation:location
completionHandler:^(NSArray *placemarks, NSError *error) {
if (!error && [placemarks count] > 0)
{
NSDictionary *dict =
[[placemarks objectAtIndex:0] addressDictionary];
NSArray *formattedLines = [dict objectForKey:@"FormattedAddressLines"];
NSString *formattedAddress = formattedLines[0];
NSLog(@"address is %@",formattedAddress);
}else{
NSLog(@"ERROR: %@", error);
}
}];
關(guān)于坐標(biāo)系的問題
最后講一下關(guān)于坐標(biāo)系的問題。
世界通用的坐標(biāo)系是WGS坐標(biāo)系齐媒,中國國測(cè)局的坐標(biāo)系是GCJ蒲每,百度有自己的坐標(biāo)系。
同樣的經(jīng)緯度應(yīng)用在不同的坐標(biāo)系會(huì)有所偏差喻括,在Github上面有一個(gè)庫可以實(shí)現(xiàn)不同坐標(biāo)系之間的轉(zhuǎn)化:
https://github.com/TinyQ/TQLocationConverter
系統(tǒng)返回的自然是根據(jù)WGS定位的邀杏。如果使用百度SDK獲取的就是Baidu坐標(biāo)系的。