iOS-定位

一、前言

大數(shù)據(jù)時(shí)代,用戶對自己的隱私安全越來越關(guān)注,所以,隨著iOS系統(tǒng)更新,蘋果對用戶隱私相關(guān)(定位、相冊、網(wǎng)絡(luò)、粘貼板等)權(quán)限控制持續(xù)升級。其中定位權(quán)限相關(guān)申請API與配置項(xiàng)較多,本文旨在記錄說明 iOS8.0 - 14.0beta 從權(quán)限申請到獲取定位數(shù)據(jù)流程。

內(nèi)容包括定位權(quán)限、獲取定位數(shù)據(jù)、定位權(quán)限API調(diào)用實(shí)踐。定位權(quán)限模塊按照系統(tǒng)相關(guān)性分別介紹該系統(tǒng)下權(quán)限配置與API調(diào)用細(xì)節(jié)、注意事項(xiàng)與表格總結(jié);獲取定位數(shù)據(jù)模塊介紹定位關(guān)鍵參數(shù)、單次/連續(xù)定位等;調(diào)用實(shí)踐模塊介紹了從 iOS8.0 - 14.0beta 系統(tǒng)定位權(quán)限的適配實(shí)踐。

二、定位權(quán)限

1、iOS8.*

-前臺定位

-需要在info.plist配置NSLocationWhenInUseUsageDescription字段;

-首次使用定位時(shí),通過API接口requestWhenInUseAuthorization申請應(yīng)用使用時(shí)權(quán)限;

注意:此權(quán)限下,如果Xcode勾選 Capabilities -> UIBackgroundModes > Location updates,則app退到后臺仍可獲取定位數(shù)據(jù),但此時(shí)在手機(jī)上方會有定位小藍(lán)條提示;

-后臺定位

-需要在info.plist配置NSLocationAlwaysUsageDescription字段;

-需要Xcode勾選 Capabilities -> UIBackgroundModes > Location updates;

-首次使用定位時(shí),通過API接口requestAlwaysAuthorization申請應(yīng)用未使用時(shí)權(quán)限;

2、iOS9.與iOS10.

-****前臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription字段;

-首次使用定位時(shí),通過API接口requestWhenInUseAuthorization申請應(yīng)用使用時(shí)權(quán)限,如下圖;

image
 注意:此權(quán)限下,如果Xcode勾選 Capabilities -> UIBackgroundModes > Location updates并且allowsBackgroundLocationUpdates設(shè)為YES,則app退到后臺仍可獲取定位數(shù)據(jù),但此時(shí)在手機(jī)上方會有定位小藍(lán)條提示;

-****后臺定位****

-需要在info.plist配置NSLocationAlwaysUsageDescription字段;

-需要Xcode勾選 Capabilities -> UIBackgroundModes > Location updates;

-需要CLLocationManager設(shè)置allowsBackgroundLocationUpdates為YES;

-首次使用定位時(shí),通過API接口requestAlwaysAuthorization申請應(yīng)用未使用時(shí)權(quán)限,如下圖;

image

-與iOS8.版本相比不同點(diǎn)***

-iOS9.后臺增加了allowsBackgroundLocationUpdates屬性,可以認(rèn)為在iOS8.下allowsBackgroundLocationUpdates永遠(yuǎn)為YES;

3、iOS11.與iOS12.

-****前臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription字段;

-首次使用定位時(shí),通過API接口requestWhenInUseAuthorization申請應(yīng)用使用時(shí)權(quán)限,如下圖;

image

注意:此權(quán)限下,如果Xcode勾選 Capabilities -> UIBackgroundModes > Location updates并且allowsBackgroundLocationUpdates設(shè)為YES,則app退到后臺仍可獲取定位數(shù)據(jù),但此時(shí)在手機(jī)上方會有定位小藍(lán)條提示,此小藍(lán)條不可隱藏;

-****后臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription字段;

-需要Xcode勾選 Capabilities -> UIBackgroundModes > Location updates;

-需要CLLocationManager設(shè)置allowsBackgroundLocationUpdates為YES;

-首次使用定位時(shí),通過API接口requestAlwaysAuthorization申請應(yīng)用未使用時(shí)權(quán)限,如下圖;

image
 注意:此權(quán)限下,當(dāng)app在后臺時(shí),系統(tǒng)默認(rèn)不展示定位小藍(lán)條,可通過showsBackgroundLocationIndicator控制小藍(lán)條是否顯示;

-與iOS10.版本相比不同點(diǎn)***

-iOS11.*變更了后臺定位權(quán)限配置字段;

-iOS11.*以后如果申請后臺定位,info.plist需要同時(shí)配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription兩個(gè)字段;

-iOS11.增加了showsBackgroundLocationIndicator屬性,當(dāng)擁有后臺定位權(quán)限時(shí),用于控制定位小藍(lán)條是否顯示。可以認(rèn)為在iOS10.之前showsBackgroundLocationIndicator永遠(yuǎn)為NO;

4、iOS13.*

-****前臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription字段;

-首次使用定位時(shí),通過API接口requestWhenInUseAuthorization申請應(yīng)用使用時(shí)權(quán)限;

注意:權(quán)限申請彈窗與之前版本不一致,新增了允許一次選項(xiàng);如果用戶選擇允許一次后,下次在使用app時(shí),仍可重新調(diào)用API申請定位權(quán)限;如下圖

image

-****后臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription字段;

-需要Xcode勾選 Capabilities -> UIBackgroundModes > Location updates;

-需要CLLocationManager設(shè)置allowsBackgroundLocationUpdates為YES;

-首次使用定位時(shí),通過API接口requestAlwaysAuthorization申請權(quán)限;

 注意:1)直接調(diào)用requestAlwaysAuthorization申請權(quán)限時(shí),權(quán)限彈窗與調(diào)用requestWhenInUseAuthorization一樣,如上圖,用戶只可以選擇應(yīng)用使用時(shí)或者只允許一次。不同點(diǎn):當(dāng)選擇使用app時(shí)允許選項(xiàng)后,狀態(tài)變更的回調(diào)為kCLAuthorizationStatusAuthorizedAlways;并且當(dāng)app退到后臺后,系統(tǒng)會擇機(jī)彈窗提示用戶是否要升級權(quán)限為始終允許。如下圖:
image
2)如果想要在應(yīng)用使用期間彈窗申請始終允許,則需要先調(diào)用requestWhenInUseAuthorization,并且獲得應(yīng)用使用期間定位權(quán)限,之后在調(diào)用requestAlwaysAuthorization則可彈窗申請始終允許,如下圖;(感覺不是太友好,不建議使用)
image

-與iOS12.版本相比不同點(diǎn)***

-使用應(yīng)用期間的定位權(quán)限增加了允許一次選項(xiàng);

-不能直接申請后臺定位權(quán)限,需要用戶先選擇應(yīng)用使用期間的定位權(quán)限后,在進(jìn)行權(quán)限升級;

5、iOS14.*(beta版本)

-****前臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription字段;

-首次使用定位時(shí),通過API接口requestWhenInUseAuthorization申請應(yīng)用使用時(shí)權(quán)限;

注意:權(quán)限申請彈窗與之前版本不一致,新增了精確位置開關(guān),新增了小地圖展示當(dāng)前位置;小地圖的顯示,支持在手機(jī)定位設(shè)置中選擇,如果選擇關(guān)閉不顯示則手機(jī)中所有app都不顯示此小地圖。如下圖

image

-****后臺定位****

-需要在info.plist配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription字段;

-需要Xcode勾選 Capabilities -> UIBackgroundModes > Location updates;

-需要CLLocationManager設(shè)置allowsBackgroundLocationUpdates為YES;

-首次使用定位時(shí),通過API接口requestAlwaysAuthorization申請權(quán)限;

-****新增精度權(quán)限****

-需要在info.plist配置NSLocationTemporaryUsageDescriptionDictionary,如下:


<key>NSLocationTemporaryUsageDescriptionDictionary</key>

<dict>

  ExampleUsageDescription

  This app needs accurate location so it can verify that you are in a supported region.

  AnotherUsageDescription

  This app needs accurate location so it can show you relevant results.

</dict>

-新增屬性字段@property (nonatomic, readonly) CLAccuracyAuthorization accuracyAuthorization API_AVAILABLE(ios(14.0), macos(11.0), watchos(7.0), tvos(14.0));可以獲取當(dāng)前的定位精度權(quán)限。

-在app已經(jīng)獲得定位權(quán)限之后,并且當(dāng)前用戶選擇的是模糊定位,則允許應(yīng)用申請一次臨時(shí)精確定位權(quán)限,申請api為- (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString )purposeKey completion:(void(^)(NSError ))completion; 其中purposeKey既為plist中配置字典中的key,可以有多個(gè),對應(yīng)app中不同的定位需求場景;注意:**此API不能用于申請定位權(quán)限,只能用于從模糊定位升級為精確定位;申請定位權(quán)限只能調(diào)用requestWhen或requestAlways,如果沒有獲得定位權(quán)限,直接調(diào)用此API無效。如下圖

image

-如果app默認(rèn)不使用精確定位,則可以在info.plist中配置NSLocationDefaultAccuracyReduced字段,配置該字段后,申請定位權(quán)限的小地圖中不在有精確定位的開關(guān),即為關(guān)。如下面圖示

  -需要注意該字段類型為Boolean,如果為其他類型則不起效;

  -配置該字段后,申請定位權(quán)限的小地圖左上角則沒有精確開關(guān),默認(rèn)關(guān)閉,如下面圖示。但是如果info.plist中配置了NSLocationTemporaryUsageDescriptionDictionary,則仍可以申請臨時(shí)的精確定位權(quán)限;

  -??:測試期間使用Xcode12 beta1到beta4,直接使用info.plist的Property List添加NSLocationDefaultAccuracyReduced字段只能是string,所以會造成不起效的問題,如果您也遇到類似問題,可以點(diǎn)擊info.plist右鍵Open As -> Source Code,即使用源碼直接添加既可起效;

<key>NSLocationDefaultAccuracyReduced</key>

<true/>

[圖片上傳失敗...(image-24baae-1637218463749)]

-與iOS13.版本相比不同點(diǎn)***

-權(quán)限申請彈窗與之前版本不一致;

-新增精度權(quán)限相關(guān)plist設(shè)置、授權(quán)、讀??;

-新增臨時(shí)一次從模糊定位升級精確定位API;

-新增定位權(quán)限變更回調(diào);

6、定位權(quán)限更新回調(diào)

-****iOS13.*及以前:****- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status;

-如主動獲取定位權(quán)限可使用類方法:+ (CLAuthorizationStatus)authorizationStatus;

-CLAuthorizationStatus枚舉取值

typedef NS_ENUM(int, CLAuthorizationStatus) {

kCLAuthorizationStatusNotDetermined = 0, //用戶沒有決定是否使用定位服務(wù)

kCLAuthorizationStatusRestricted, //定位服務(wù)授權(quán)狀態(tài)受限制

kCLAuthorizationStatusDenied, //用戶拒絕/定位總開關(guān)關(guān)閉

kCLAuthorizationStatusAuthorizedAlways, //始終允許

kCLAuthorizationStatusAuthorizedWhenInUse, //在應(yīng)用使用期間

kCLAuthorizationStatusAuthorized //已經(jīng)廢棄,等同于始終允許

};

-iOS14.及以后***:- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager;

-通過manager.authorizationStatus對象方法獲取當(dāng)前定位權(quán)限,此方法在iOS13及以前版本是類方法;

-通過manager.accuracyAuthorization對象方法獲取當(dāng)前精度權(quán)限;

-CLAccuracyAuthorization枚舉取值

typedef NS_ENUM(NSInteger, CLAccuracyAuthorization) {

CLAccuracyAuthorizationFullAccuracy, //精確定位

CLAccuracyAuthorizationReducedAccuracy, //模糊定位

};

7、總結(jié)

-****定位權(quán)限注意事項(xiàng)****

-iOS11以后如果申請后臺定位,info.plist需要同時(shí)配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription兩個(gè)字段;

-調(diào)用申請定位權(quán)限API,在用戶抉擇后,再次調(diào)用無效;

-如果調(diào)用requestWhenInUseAuthorization申請過使用期間的定位權(quán)限,并且得到用戶許可,則之后仍可調(diào)用requestAlwaysAuthorization申請一次后臺定位權(quán)限(即權(quán)限升級);

-iOS13后,直接調(diào)用requestAlwaysAuthorization申請權(quán)限時(shí),權(quán)限彈窗與調(diào)用requestWhenInUseAuthorization一樣,在app進(jìn)入后臺后,系統(tǒng)會擇機(jī)彈窗提示用戶是否要權(quán)限升級為始終允許;

-自2019年下半年起,蘋果商店上架app對后臺定位權(quán)限增加限制,如果info.plist中不包含NSLocationAlwaysUsageDescription/NSLocationAlwaysAndWhenInUseUsageDescription字段,則在app代碼中不能出現(xiàn)符號requestAlwaysAuthorization,否則上架審核不通過;

-****Info.plist 中的字段總結(jié)****

| iOS版本 | NSLocationWhenInUseUsageDescription | NSLocationAlwaysUsageDescription | NSLocationAlwaysAndWhenInUseUsageDescription | NSLocationTemporaryUsageDescriptionDictionary |

| :---: | :---: | :---: | :---: | :---: |

| iOS 8 | YES | YES | × | × |

| iOS 9 | YES | YES | × | × |

| iOS 10 | YES | YES | × | × |

| iOS 11 | YES | × | YES | × |

| iOS 12 | YES | × | YES | × |

| iOS 13 | YES | × | YES | × |

| iOS 14 | YES | × | YES | YES |

-****不同系統(tǒng)版本調(diào)用定位權(quán)限API差異****

****iOS8.0****

| - | Capabilities 關(guān) | Capabilities 開 |

| :--- | :---: | :---: |

| requestAlwaysAuthorization | 可以前臺定位、不可以后臺定位、無藍(lán)條 | 可以前臺定位、可以后臺定位、無藍(lán)條 |

| requestWhenInUseAuthorization | 可以前臺定位、不可以后臺定位、無藍(lán)條 | 可以前臺定位、可以后臺定位、有藍(lán)條 |

| 無/用戶拒絕 | 無任何定位 | 無任何定位 |

****iOS9.0 - iOS12.0****

| | Capabilities 關(guān) | | Capabilities 開 | |

| --- | :---: | :---: | --- | --- |

| | allowsBackgroundLocationUpdates關(guān) | allowsBackgroundLocationUpdates開 | allowsBackgroundLocationUpdates關(guān) | allowsBackgroundLocationUpdates開 |

| requestAlwaysAuthorization | 可以前臺定位、不可以后臺定位、無藍(lán)條 | iOS拋出Crash | 可以前臺定位、不可以后臺定位、無藍(lán)條 | 可以前臺定位、可以后臺定位、無藍(lán)條 |

| requestWhenInUseAuthorization | 可以前臺定位、不可以后臺定位、無藍(lán)條 | iOS拋出Crash | 可以前臺定位、不可以后臺定位、無藍(lán)條 | 可以前臺定位、可以后臺定位、有藍(lán)條 |

| 無/用戶拒絕 | 無任何定位 | iOS拋出Crash | 無任何定位 | 無任何定位 |

三、獲取定位數(shù)據(jù)

1、單次定位

-iOS8.0版本不支持單次定位,需要調(diào)用連續(xù)定位startUpdatingLocation接口,自行實(shí)現(xiàn)單次定位功能;

-iOS9.0及以后版本,可以調(diào)用單次定位API:

-(void)requestLocation API_AVAILABLE(ios(9.0));

2、連續(xù)定位

-開始連續(xù)定位:- (void)startUpdatingHeading;

-停止連續(xù)定位:- (void)stopUpdatingHeading;

3、定位CLLocationManager相關(guān)屬性

-定位活動類型@property(assign, nonatomic) CLActivityType activityType;

-typedef NS_ENUM(NSInteger, CLActivityType) {

CLActivityTypeOther = 1, //未知類型,默認(rèn)值

CLActivityTypeAutomotiveNavigation, //駕車導(dǎo)航定位

CLActivityTypeFitness,                        //健身活動,如步行、跑步、騎車等;

CLActivityTypeOtherNavigation, //其他交通工具導(dǎo)航,如火車、輪船等

CLActivityTypeAirborne                       //空中飛行定位(iOS12及以上版本)

};

-設(shè)置期望的定位精度@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;

-當(dāng)精度設(shè)置較高時(shí),定位服務(wù)會盡可能去獲取滿足desiredAccuracy的定位結(jié)果,但不一定會得到滿足期望的結(jié)果;

-kCLLocationAccuracyReduced為iOS14新特性,模糊定位,即使當(dāng)前精確定位開啟,如果設(shè)置該值,則會收到模糊定位結(jié)果;

-取值范圍:

kCLLocationAccuracyBestForNavigation; //導(dǎo)航高精度

kCLLocationAccuracyBest; //高精度

kCLLocationAccuracyNearestTenMeters; //10米

kCLLocationAccuracyHundredMeters; //100米

kCLLocationAccuracyKilometer; //1000米

kCLLocationAccuracyThreeKilometers; //3000米

kCLLocationAccuracyReduced; //模糊定位,誤差5000米(iOS14及以上版本)

-設(shè)置定位的最小更新距離@property(assign, nonatomic) CLLocationDistance distanceFilter;

-單位米,默認(rèn)為 kCLDistanceFilterNone,表示只要檢測到設(shè)備位置發(fā)生變化就會更新位置信息;

-@property(nonatomic, assign) BOOL pausesLocationUpdatesAutomatically;

-是否允許系統(tǒng)自動暫停定位功能,設(shè)置為YES進(jìn)行后臺定位時(shí),系統(tǒng)檢測到長時(shí)間沒有位置更新的時(shí)候,將會暫停定位功能,當(dāng)app進(jìn)入前臺時(shí)會恢復(fù)定位功能;

3、定位數(shù)據(jù)更新回調(diào)

--(void)locationManager:(CLLocationManager *)manager

didUpdateLocations:(NSArray *)locations;

-locations是按時(shí)間排序的CLLocation對象數(shù)組,一般使用lastObject即為當(dāng)前最新定位信息;

四、定位權(quán)限API調(diào)用實(shí)踐

1、配置info.plist

-如果不需要使用后臺定位,則無需配置NSLocationAlwaysAndWhenInUseUsageDescription、NSLocationAlwaysUsageDescription字段,并且代碼(包括使用的靜態(tài)庫)中不能出現(xiàn)requestAlwaysAuthorization符號;

[圖片上傳失敗...(image-3fd3da-1637218463749)]

2、開始定位

-此處直接在主線程開始定位,如果需要在子線程開始定位,則需要開啟子線程的runloop,此處不再累述。自蘋果X后,如果在子線程開始定位,會有UI不在主線程調(diào)用的警告,直接屏蔽或者忽略即可,不影響正常使用;


//前置步驟:創(chuàng)建定位管理類CLLocationManager,配置定位參數(shù)

//開始定位

- (void)startLocation{

//self.locationManager = [[CLLocationManager alloc]init];

//self.locationManager.allowsBackgroundLocationUpdates = YES;

//self.locationManager.delegate = self;

    if([self locationServiceIsValid] == NO){

        NSLog(@"用戶拒絕該app使用定位服務(wù)");

        return;

    }

    //該場景下是否需要精確定位

    BOOL isNeedFullAccuracy = YES;

    //該場景下如果需要精確定位,則對應(yīng)的plist中配置的key

    NSString *purposeKey = @"ExampleUsageDescription";

    //判斷當(dāng)前定位權(quán)限是否ok

    [self checkLocationAuthorizationStatus:self.locationManager

                           needFullAccuracy:isNeedFullAccuracy

                                 purposeKey:purposeKey];

    //開始連續(xù)定位

    [self.locationManager startUpdatingLocation];

}

3、獲取當(dāng)前定位權(quán)限


//獲取當(dāng)前定位權(quán)限

- (CLAuthorizationStatus)authorizationStatus

{

    if (@available(iOS 14.0, *)) {

        return self.locationManager.authorizationStatus;

    } else {

        return [CLLocationManager authorizationStatus];

    }

}

//當(dāng)前應(yīng)用是否可以使用/申請定位服務(wù)

- (BOOL)locationServiceIsValid{

    if ([self authorizationStatus] == kCLAuthorizationStatusDenied ||

        [self authorizationStatus] == kCLAuthorizationStatusRestricted) {

        return NO;

    }

    return YES;

}

//當(dāng)前定位狀態(tài)是否可用

- (BOOL)locationAuthStatusIsValid{

    if ([self locationServiceIsValid] == NO) {

        return  NO;

    }

    if ([self authorizationStatus] == kCLAuthorizationStatusNotDetermined) {

        return NO;

    }

    return YES;

}

4、核實(shí)當(dāng)前權(quán)限狀態(tài),判斷是否需要申請權(quán)限或者權(quán)限升級

-****如果app需要使用后臺定位****


//核實(shí)當(dāng)前權(quán)限狀態(tài),判斷是否需要申請權(quán)限或者權(quán)限升級

- (void)checkLocationAuthorizationStatus:(CLLocationManager *)manager

                         needFullAccuracy:(BOOL)isNeedFullAccuracy

                                purposeKey:(NSString *)purposeKey{

      if([self authorizationStatus] == kCLAuthorizationStatusNotDetermined){

          //如果沒有定位權(quán)限,則需要先申請定位權(quán)限

          //如果是iOS14申請權(quán)限彈窗時(shí)可以選擇精度開關(guān),所以不用在單獨(dú)處理精度權(quán)限

          [self requestLocationAuthorizationIfNeed:manager];

      }else if(isNeedFullAccuracy){

          //如果已經(jīng)有定位權(quán)限且需要精確定位

            [self requestTemporaryFullAccuracyAuthorizationIfNeed:manager purposeKey:purposeKey];

      }

}

//如果當(dāng)前場景需要精確定位,則可以申請一次臨時(shí)精確定位

- (void)requestTemporaryFullAccuracyAuthorizationIfNeed:(CLLocationManager *)manager

                                             purposeKey:(NSString *)purposeKey

{

    //如果是非iOS14系統(tǒng),則默認(rèn)為精確定位

    if (@available(iOS 14.0, *)) {

        //如果已經(jīng)獲得定位權(quán)限,但精度權(quán)限只是模糊定位

        if (manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy) {

            NSDictionary *locationTemporaryDictionary = [[NSBundle mainBundle]

                        objectForInfoDictionaryKey:@"NSLocationTemporaryUsageDescriptionDictionary"];

            BOOL hasLocationTemporaryKey = locationTemporaryDictionary != nil && locationTemporaryDictionary.count != 0;

            if (hasLocationTemporaryKey) {

                //此API不能用于申請定位權(quán)限,只能用于從模糊定位升級為精確定位;申請定位權(quán)限只能調(diào)用

                //requestWhen或requestAlways,如果沒有獲得定位權(quán)限,直接調(diào)用此API無效。

                [manager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purposeKey completion:nil];

            }else{

                NSLog(@"如果需要使用臨時(shí)精確定位,需要在Info.plist中添加 \

                NSLocationTemporaryUsageDescriptionDictionary字段。");

            }

        }

    }

 }

//請求定位權(quán)限,

- (void)requestLocationAuthorizationIfNeed:(CLLocationManager *)manager

{

    //系統(tǒng)版本號

    CGFloat systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];

    //系統(tǒng)版本8+ && 沒有選擇過定位權(quán)限

    if (systemVersion > 7.99 && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)

    {

        //獲取info.plist中配置字段信息

        BOOL hasAlwaysKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] != nil;

        BOOL hasWhenInUseKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil;

        BOOL hasAlwaysAndWhenInUseKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"] != nil;

        //如果是iOS11及以后版本。(當(dāng)前iOS11到13居多)

        if (@available(iOS 11.0, *)){

            if (hasAlwaysAndWhenInUseKey && hasWhenInUseKey)

            {

                //如果plist同時(shí)配置兩個(gè)字段,則兩個(gè)權(quán)限申請API都可以調(diào)用;

                //建議直接調(diào)用requestAlwaysAuthorization即可

                [manager requestAlwaysAuthorization];

            }

            else if (hasWhenInUseKey)

            {

                //如果plist只配置InUseKey,則只能調(diào)用使用時(shí)API

                [manager requestWhenInUseAuthorization];

            }

            else{

                NSLog(@"要在iOS11及以上版本使用定位服務(wù), 需要在Info.plist中添加 \

                NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段。");

            }

        }

        else

        {

            if (hasAlwaysKey)

            {

                //如果plist配置hasAlwaysKey,則可以調(diào)用始終允許API

                [manager requestAlwaysAuthorization];

            }

            else if (hasWhenInUseKey)

            {

                //如果plist配置hasAlwaysKey,則可以調(diào)用始終允許API

                [manager requestWhenInUseAuthorization];

            }

            else

            {

                NSLog(@"要在iOS8到iOS10版本使用定位服務(wù), 需要在Info.plist中添加 \

                NSLocationAlwaysUsageDescription或者NSLocationWhenInUseUsageDescription字段。");

            }

        }

    }

}

-****如果app不需要使用后臺定位****


//核實(shí)當(dāng)前權(quán)限狀態(tài),判斷是否需要申請權(quán)限或者權(quán)限升級

- (void)checkLocationAuthorizationStatus:(CLLocationManager *)manager

                         needFullAccuracy:(BOOL)isNeedFullAccuracy

                                purposeKey:(NSString *)purposeKey{

      if([self authorizationStatus] == kCLAuthorizationStatusNotDetermined){

          //如果沒有定位權(quán)限,則需要先申請定位權(quán)限

          //如果是iOS14申請權(quán)限彈窗時(shí)可以選擇精度開關(guān),所以不用在單獨(dú)處理精度權(quán)限

          [self requestLocationAuthorizationIfNeed:manager];

      }else if(isNeedFullAccuracy){

          //如果已經(jīng)有定位權(quán)限且需要精確定位

            [self requestTemporaryFullAccuracyAuthorizationIfNeed:manager purposeKey:purposeKey];

      }

}

//如果當(dāng)前場景需要精確定位,則可以申請一次臨時(shí)精確定位

- (void)requestTemporaryFullAccuracyAuthorizationIfNeed:(CLLocationManager *)manager

                                             purposeKey:(NSString *)purposeKey

{

    //如果是非iOS14系統(tǒng),則默認(rèn)為精確定位

    if (@available(iOS 14.0, *)) {

        //如果已經(jīng)獲得定位權(quán)限,但精度權(quán)限只是模糊定位

        if (manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy) {

            NSDictionary *locationTemporaryDictionary = [[NSBundle mainBundle]

                        objectForInfoDictionaryKey:@"NSLocationTemporaryUsageDescriptionDictionary"];

            BOOL hasLocationTemporaryKey = locationTemporaryDictionary != nil && locationTemporaryDictionary.count != 0;

            if (hasLocationTemporaryKey) {

                //此API不能用于申請定位權(quán)限,只能用于從模糊定位升級為精確定位;申請定位權(quán)限只能調(diào)用

                //requestWhen或requestAlways,如果沒有獲得定位權(quán)限,直接調(diào)用此API無效。

                [manager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purposeKey completion:nil];

            }else{

                NSLog(@"如果需要使用臨時(shí)精確定位,需要在Info.plist中添加 \

                NSLocationTemporaryUsageDescriptionDictionary字段。");

            }

        }

    }

 }

//請求定位權(quán)限,

- (void)requestLocationAuthorizationIfNeed:(CLLocationManager *)manager

{

    //系統(tǒng)版本號

    CGFloat systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];

    //系統(tǒng)版本8+ && 沒有選擇過定位權(quán)限

    if (systemVersion > 7.99 && [CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)

    {

        //獲取info.plist中配置字段信息

        BOOL hasWhenInUseKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil;

        if (hasWhenInUseKey)

        {

            //如果plist配置InUseKey,則只能調(diào)用使用時(shí)API

            [manager requestWhenInUseAuthorization];

        }

        else{

            NSLog(@"要在iOS8及以上版本使用定位服務(wù), 需要在Info.plist中添加 \

            NSLocationWhenInUseUsageDescription字段。");

        }

    }

}

5、定位權(quán)限狀態(tài)變更


//iOS13及以前版本回調(diào)

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status

{

    [self locationStatusDidChanged:[CLLocationManager authorizationStatus]];

}

//iOS14及以后版本回調(diào)

- (void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager

{

    if (@available(iOS 14.0, *)) {

        [self locationStatusDidChanged:manager.authorizationStatus];

        if([self locationAuthStatusIsValid]){

            CLAccuracyAuthorization accuracyAuth = manager.accuracyAuthorization;

            if (accuracyAuth == CLAccuracyAuthorizationReducedAccuracy){

                NSLog(@"TODO: 可以模糊定位");

            }else{

                NSLog(@"TODO: 可以精確定位");

            }

            //該場景下是否需要精確定位

            BOOL isNeedFullAccuracy = YES;

            if (isNeedFullAccuracy == YES && accuracyAuth == CLAccuracyAuthorizationReducedAccuracy) {

                NSLog(@"TODO: 該場景需要精確定位才可以使用,請去設(shè)置中打開精確定位開關(guān)");

            }

        }

    } else {

    }

}

- (void)locationStatusDidChanged:(CLAuthorizationStatus)authStatus

{

    switch (authStatus) {

        case kCLAuthorizationStatusNotDetermined:

            NSLog(@"可以申請定位權(quán)限");

            break;

        case kCLAuthorizationStatusRestricted:

        case kCLAuthorizationStatusDenied:

            NSLog(@"TODO: 沒有定位權(quán)限");

            break;

        case kCLAuthorizationStatusAuthorizedAlways:

        case kCLAuthorizationStatusAuthorizedWhenInUse:

            NSLog(@"TODO: 擁有定位權(quán)限");

        default:

            break;

    }

}

6、定位回調(diào)


//定位回調(diào)

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations

{

    CLLocation *locationg = locations.lastObject;

    NSLog(@"TODO: 收到定位數(shù)據(jù):%@",locationg);

}

五、小結(jié)

定位信息作為用戶非常在意的隱私數(shù)據(jù),iOS開發(fā)者應(yīng)盡量遵循適用原則(即能滿足需求的最小權(quán)限)去獲取用戶定位信息。本文對iOS系統(tǒng)定位權(quán)限說明從8.0到14.0,其中關(guān)于API調(diào)用實(shí)踐是對應(yīng)的最大定位權(quán)限,開發(fā)者可以根據(jù)需求參考相對應(yīng)的部分。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容