iOS定位权限与使用阐述 (含iOS14定位适配)

### 一、前言

    大数据时代,用户对自己的隐私安全越来越关注,所以,随着iOS系统更新,苹果对用户隐私相关(定位、相册、网络、粘贴板等)权限控制持续升级。其中定位权限相关申请API与配置项较多,本文旨在记录说明 iOS8.0 - 14.0beta 从权限申请到获取定位数据流程。

    内容包括定位权限、获取定位数据、定位权限API调用实践。定位权限模块按照系统相关性分别介绍该系统下权限配置与API调用细节、注意事项与表格总结;获取定位数据模块介绍定位关键参数、单次/连续定位等;调用实践模块介绍了从 iOS8.0 - 14.0beta 系统定位权限的适配实践。

### 二、定位权限

#### 1、iOS8.*

-前台定位

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

   -首次使用定位时,通过API接口requestWhenInUseAuthorization申请应用使用时权限;

注意:此权限下,如果Xcode勾选 Capabilities -> UIBackgroundModes > Location updates,则app退到后台仍可获取定位数据,但此时在手机上方会有定位小蓝条提示;

-后台定位

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

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

   -首次使用定位时,通过API接口requestAlwaysAuthorization申请应用未使用时权限;

#### 2、iOS9.*与iOS10.*

-**前台定位**

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

   -首次使用定位时,通过API接口requestWhenInUseAuthorization申请应用使用时权限,如下图;


     注意:此权限下,如果Xcode勾选 Capabilities -> UIBackgroundModes > Location updates并且allowsBackgroundLocationUpdates设为YES,则app退到后台仍可获取定位数据,但此时在手机上方会有定位小蓝条提示;

-**后台定位**

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

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

   -需要CLLocationManager设置allowsBackgroundLocationUpdates为YES;

   -首次使用定位时,通过API接口requestAlwaysAuthorization申请应用未使用时权限,如下图;


-**与iOS8.*版本相比不同点**

   -iOS9.*后台增加了allowsBackgroundLocationUpdates属性,可以认为在iOS8.*下allowsBackgroundLocationUpdates永远为YES;

#### 3、iOS11.*与iOS12.*

-**前台定位**

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

   -首次使用定位时,通过API接口requestWhenInUseAuthorization申请应用使用时权限,如下图;


注意:此权限下,如果Xcode勾选 Capabilities -> UIBackgroundModes > Location updates并且allowsBackgroundLocationUpdates设为YES,则app退到后台仍可获取定位数据,但此时在手机上方会有定位小蓝条提示,此小蓝条不可隐藏;

-**后台定位**

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

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

   -需要CLLocationManager设置allowsBackgroundLocationUpdates为YES;

   -首次使用定位时,通过API接口requestAlwaysAuthorization申请应用未使用时权限,如下图;


     注意:此权限下,当app在后台时,系统默认不展示定位小蓝条,可通过showsBackgroundLocationIndicator控制小蓝条是否显示;

-**与iOS10.*版本相比不同点**

   -iOS11.*变更了后台定位权限配置字段;

   -iOS11.*以后如果申请后台定位,info.plist需要同时配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription两个字段;

   -iOS11.*增加了showsBackgroundLocationIndicator属性,当拥有后台定位权限时,用于控制定位小蓝条是否显示。可以认为在iOS10.*之前showsBackgroundLocationIndicator永远为NO;

#### 4、iOS13.*

-**前台定位**

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

   -首次使用定位时,通过API接口requestWhenInUseAuthorization申请应用使用时权限;

注意:权限申请弹窗与之前版本不一致,新增了允许一次选项;如果用户选择允许一次后,下次在使用app时,仍可重新调用API申请定位权限;如下图


-**后台定位**

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

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

   -需要CLLocationManager设置allowsBackgroundLocationUpdates为YES;

   -首次使用定位时,通过API接口requestAlwaysAuthorization申请权限;

     注意:1)直接调用requestAlwaysAuthorization申请权限时,权限弹窗与调用requestWhenInUseAuthorization一样,如上图,用户只可以选择应用使用时或者只允许一次。不同点:当选择使用app时允许选项后,状态变更的回调为kCLAuthorizationStatusAuthorizedAlways;并且当app退到后台后,系统会择机弹窗提示用户是否要升级权限为始终允许。如下图:


    2)如果想要在应用使用期间弹窗申请始终允许,则需要先调用requestWhenInUseAuthorization,并且获得应用使用期间定位权限,之后在调用requestAlwaysAuthorization则可弹窗申请始终允许,如下图;(感觉不是太友好,不建议使用)


-**与iOS12.*版本相比不同点**

   -使用应用期间的定位权限增加了允许一次选项;

   -不能直接申请后台定位权限,需要用户先选择应用使用期间的定位权限后,在进行权限升级;

#### 5、iOS14.*(beta版本)

-**前台定位**

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

   -首次使用定位时,通过API接口requestWhenInUseAuthorization申请应用使用时权限;

注意:权限申请弹窗与之前版本不一致,新增了精确位置开关,新增了小地图展示当前位置;小地图的显示,支持在手机定位设置中选择,如果选择关闭不显示则手机中所有app都不显示此小地图。如下图


-**后台定位**

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

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

   -需要CLLocationManager设置allowsBackgroundLocationUpdates为YES;

   -首次使用定位时,通过API接口requestAlwaysAuthorization申请权限;

-**新增精度权限**

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

```objectivec

<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));可以获取当前的定位精度权限。

   -在app已经获得定位权限之后,并且当前用户选择的是模糊定位,则允许应用申请一次临时精确定位权限,申请api为- (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey completion:(void(^)(NSError *))completion; 其中purposeKey既为plist中配置字典中的key,可以有多个,对应app中不同的定位需求场景;**注意:**此API不能用于申请定位权限,只能用于从模糊定位升级为精确定位;申请定位权限只能调用requestWhen或requestAlways,如果没有获得定位权限,直接调用此API无效。如下图


   -如果app默认不使用精确定位,则可以在info.plist中配置NSLocationDefaultAccuracyReduced字段,配置该字段后,申请定位权限的小地图中不在有精确定位的开关,即为关。如下面图示

      -需要注意该字段类型为Boolean,如果为其他类型则不起效;

      -配置该字段后,申请定位权限的小地图左上角则没有精确开关,默认关闭,如下面图示。但是如果info.plist中配置了NSLocationTemporaryUsageDescriptionDictionary,则仍可以申请临时的精确定位权限;

      -⚠️:测试期间使用Xcode12 beta1到beta4,直接使用info.plist的Property List添加NSLocationDefaultAccuracyReduced字段只能是string,所以会造成不起效的问题,如果您也遇到类似问题,可以点击info.plist右键Open As -> Source Code,即使用源码直接添加既可起效;

```objectivec

<key>NSLocationDefaultAccuracyReduced</key>

<true/>

```

![IMG_5624.PNG](https://intranetproxy.alipay.com/skylark/lark/0/2020/png/167960/1597995990109-af75293a-f741-424b-bca1-38e825065de7.png#align=left&display=inline&height=346&margin=%5Bobject%20Object%5D&name=IMG_5624.PNG&originHeight=2436&originWidth=1125&size=2638748&status=done&style=none&width=160)

-**与iOS13.*版本相比不同点**

   -权限申请弹窗与之前版本不一致;

   -新增精度权限相关plist设置、授权、读取;

   -新增临时一次从模糊定位升级精确定位API;

   -新增定位权限变更回调;

#### 6、定位权限更新回调

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

   -如主动获取定位权限可使用类方法:+ (CLAuthorizationStatus)authorizationStatus;

   -CLAuthorizationStatus枚举取值

typedef NS_ENUM(int, CLAuthorizationStatus) {

  kCLAuthorizationStatusNotDetermined = 0,    //用户没有决定是否使用定位服务

kCLAuthorizationStatusRestricted,                //定位服务授权状态受限制

kCLAuthorizationStatusDenied,                     //用户拒绝/定位总开关关闭

kCLAuthorizationStatusAuthorizedAlways,      //始终允许

kCLAuthorizationStatusAuthorizedWhenInUse, //在应用使用期间

kCLAuthorizationStatusAuthorized                //已经废弃,等同于始终允许

};

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

   -通过manager.authorizationStatus对象方法获取当前定位权限,此方法在iOS13及以前版本是类方法;

   -通过manager.accuracyAuthorization对象方法获取当前精度权限;

   -CLAccuracyAuthorization枚举取值

typedef NS_ENUM(NSInteger, CLAccuracyAuthorization) {

CLAccuracyAuthorizationFullAccuracy,          //精确定位

CLAccuracyAuthorizationReducedAccuracy,    //模糊定位

};

####7、总结

-**定位权限注意事项**

   -iOS11以后如果申请后台定位,info.plist需要同时配置NSLocationWhenInUseUsageDescription、NSLocationAlwaysAndWhenInUseUsageDescription两个字段;

   -调用申请定位权限API,在用户抉择后,再次调用无效;

   -如果调用requestWhenInUseAuthorization申请过使用期间的定位权限,并且得到用户许可,则之后仍可调用requestAlwaysAuthorization申请一次后台定位权限(即权限升级);

   -iOS13后,直接调用requestAlwaysAuthorization申请权限时,权限弹窗与调用requestWhenInUseAuthorization一样,在app进入后台后,系统会择机弹窗提示用户是否要权限升级为始终允许;

   -自2019年下半年起,苹果商店上架app对后台定位权限增加限制,如果info.plist中不包含NSLocationAlwaysUsageDescription/NSLocationAlwaysAndWhenInUseUsageDescription字段,则在app代码中不能出现符号requestAlwaysAuthorization,否则上架审核不通过;

-**Info.plist 中的字段总结**

| 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 |

-**不同系统版本调用定位权限API差异**

**iOS8.0**

| - | Capabilities 关 | Capabilities 开 |

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

| requestAlwaysAuthorization  | 可以前台定位、不可以后台定位、无蓝条  | 可以前台定位、可以后台定位、无蓝条 |

| requestWhenInUseAuthorization  | 可以前台定位、不可以后台定位、无蓝条  | 可以前台定位、可以后台定位、有蓝条 |

| 无/用户拒绝  | 无任何定位  | 无任何定位 |

**iOS9.0 - iOS12.0**

|  | Capabilities 关 |  | Capabilities 开 |  |

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

|  | allowsBackgroundLocationUpdates关  | allowsBackgroundLocationUpdates开 | allowsBackgroundLocationUpdates关 | allowsBackgroundLocationUpdates开 |

| requestAlwaysAuthorization | 可以前台定位、不可以后台定位、无蓝条  | iOS抛出Crash  | 可以前台定位、不可以后台定位、无蓝条 | 可以前台定位、可以后台定位、无蓝条 |

| requestWhenInUseAuthorization | 可以前台定位、不可以后台定位、无蓝条  | iOS抛出Crash  | 可以前台定位、不可以后台定位、无蓝条 | 可以前台定位、可以后台定位、有蓝条 |

| 无/用户拒绝 | 无任何定位  | iOS抛出Crash  | 无任何定位 | 无任何定位 |

### 三、获取定位数据

#### 1、单次定位

-iOS8.0版本不支持单次定位,需要调用连续定位startUpdatingLocation接口,自行实现单次定位功能;

-iOS9.0及以后版本,可以调用单次定位API: 

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

#### 2、连续定位

-开始连续定位:- (void)startUpdatingHeading;

-停止连续定位:- (void)stopUpdatingHeading;

#### 3、定位CLLocationManager相关属性

-定位活动类型@property(assign, nonatomic) CLActivityType activityType; 

   -typedef NS_ENUM(NSInteger, CLActivityType) {

CLActivityTypeOther = 1,  //未知类型,默认值

CLActivityTypeAutomotiveNavigation,    //驾车导航定位

    CLActivityTypeFitness,                        //健身活动,如步行、跑步、骑车等;

CLActivityTypeOtherNavigation,    //其他交通工具导航,如火车、轮船等

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

};

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

   -当精度设置较高时,定位服务会尽可能去获取满足desiredAccuracy的定位结果,但不一定会得到满足期望的结果;

   -kCLLocationAccuracyReduced为iOS14新特性,模糊定位,即使当前精确定位开启,如果设置该值,则会收到模糊定位结果;

   -取值范围:

kCLLocationAccuracyBestForNavigation;    //导航高精度

kCLLocationAccuracyBest;                      //高精度

kCLLocationAccuracyNearestTenMeters;    //10米

kCLLocationAccuracyHundredMeters;        //100米

kCLLocationAccuracyKilometer;    //1000米

kCLLocationAccuracyThreeKilometers;      //3000米

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

-设置定位的最小更新距离@property(assign, nonatomic) CLLocationDistance distanceFilter;

   -单位米,默认为 kCLDistanceFilterNone,表示只要检测到设备位置发生变化就会更新位置信息;

-@property(nonatomic, assign) BOOL pausesLocationUpdatesAutomatically;

   -是否允许系统自动暂停定位功能,设置为YES进行后台定位时,系统检测到长时间没有位置更新的时候,将会暂停定位功能,当app进入前台时会恢复定位功能;

#### 3、定位数据更新回调

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

didUpdateLocations:(NSArray *)locations;

   -locations是按时间排序的CLLocation对象数组,一般使用lastObject即为当前最新定位信息;

### 四、定位权限API调用实践

#### 1、配置info.plist

-如果不需要使用后台定位,则无需配置NSLocationAlwaysAndWhenInUseUsageDescription、NSLocationAlwaysUsageDescription字段,并且代码(包括使用的静态库)中不能出现requestAlwaysAuthorization符号;

  ![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2020/png/167960/1597642988426-7b05fa25-bae3-420a-afb5-d9222729338b.png#align=left&display=inline&height=110&margin=%5Bobject%20Object%5D&name=image.png&originHeight=219&originWidth=1067&size=160854&status=done&style=none&width=533.5)

#### 2、开始定位

-此处直接在主线程开始定位,如果需要在子线程开始定位,则需要开启子线程的runloop,此处不再累述。自苹果X后,如果在子线程开始定位,会有UI不在主线程调用的警告,直接屏蔽或者忽略即可,不影响正常使用;

```objectivec

//前置步骤:创建定位管理类CLLocationManager,配置定位参数

//开始定位

- (void)startLocation{

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

//self.locationManager.allowsBackgroundLocationUpdates = YES;

//self.locationManager.delegate = self;

    if([self locationServiceIsValid] == NO){

        NSLog(@"用户拒绝该app使用定位服务");

        return;

    }

    //该场景下是否需要精确定位

    BOOL isNeedFullAccuracy = YES;

    //该场景下如果需要精确定位,则对应的plist中配置的key

    NSString *purposeKey = @"ExampleUsageDescription";

    //判断当前定位权限是否ok

    [self checkLocationAuthorizationStatus:self.locationManager

                           needFullAccuracy:isNeedFullAccuracy

                                 purposeKey:purposeKey];


    //开始连续定位

    [self.locationManager startUpdatingLocation];

}

```

#### 3、获取当前定位权限

```objectivec

//获取当前定位权限

- (CLAuthorizationStatus)authorizationStatus

{

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

        return self.locationManager.authorizationStatus;

    } else {

        return [CLLocationManager authorizationStatus];

    }

}

//当前应用是否可以使用/申请定位服务

- (BOOL)locationServiceIsValid{

    if ([self authorizationStatus] == kCLAuthorizationStatusDenied ||

        [self authorizationStatus] == kCLAuthorizationStatusRestricted) {

        return NO;

    }

    return YES;

}

//当前定位状态是否可用

- (BOOL)locationAuthStatusIsValid{

    if ([self locationServiceIsValid] == NO) {

        return  NO;

    }

    if ([self authorizationStatus] == kCLAuthorizationStatusNotDetermined) {

        return NO;

    }

    return YES;

}

```

#### 4、核实当前权限状态,判断是否需要申请权限或者权限升级

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

```objectivec

//核实当前权限状态,判断是否需要申请权限或者权限升级

- (void)checkLocationAuthorizationStatus:(CLLocationManager *)manager

                         needFullAccuracy:(BOOL)isNeedFullAccuracy

                                purposeKey:(NSString *)purposeKey{

      if([self authorizationStatus] == kCLAuthorizationStatusNotDetermined){

          //如果没有定位权限,则需要先申请定位权限

          //如果是iOS14申请权限弹窗时可以选择精度开关,所以不用在单独处理精度权限

          [self requestLocationAuthorizationIfNeed:manager];

      }else if(isNeedFullAccuracy){

          //如果已经有定位权限且需要精确定位

            [self requestTemporaryFullAccuracyAuthorizationIfNeed:manager purposeKey:purposeKey];

      }

}

//如果当前场景需要精确定位,则可以申请一次临时精确定位

- (void)requestTemporaryFullAccuracyAuthorizationIfNeed:(CLLocationManager *)manager

                                             purposeKey:(NSString *)purposeKey

{

    //如果是非iOS14系统,则默认为精确定位

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

        //如果已经获得定位权限,但精度权限只是模糊定位

        if (manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy) {

            NSDictionary *locationTemporaryDictionary = [[NSBundle mainBundle]

                        objectForInfoDictionaryKey:@"NSLocationTemporaryUsageDescriptionDictionary"];

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

            if (hasLocationTemporaryKey) {

                //此API不能用于申请定位权限,只能用于从模糊定位升级为精确定位;申请定位权限只能调用

                //requestWhen或requestAlways,如果没有获得定位权限,直接调用此API无效。

                [manager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purposeKey completion:nil];

            }else{

                NSLog(@"如果需要使用临时精确定位,需要在Info.plist中添加 \

                NSLocationTemporaryUsageDescriptionDictionary字段。");

            }

        }

    }

 }

//请求定位权限,

- (void)requestLocationAuthorizationIfNeed:(CLLocationManager *)manager

{

    //系统版本号

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

    //系统版本8+ && 没有选择过定位权限

    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及以后版本。(当前iOS11到13居多)

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

            if (hasAlwaysAndWhenInUseKey && hasWhenInUseKey)

            {

                //如果plist同时配置两个字段,则两个权限申请API都可以调用;

                //建议直接调用requestAlwaysAuthorization即可

                [manager requestAlwaysAuthorization];

            }

            else if (hasWhenInUseKey)

            {

                //如果plist只配置InUseKey,则只能调用使用时API

                [manager requestWhenInUseAuthorization];

            }

            else{

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

                NSLocationAlwaysAndWhenInUseUsageDescription和NSLocationWhenInUseUsageDescription字段。");

            }

        }

        else

        {

            if (hasAlwaysKey)

            {

                //如果plist配置hasAlwaysKey,则可以调用始终允许API

                [manager requestAlwaysAuthorization];

            }

            else if (hasWhenInUseKey)

            {

                //如果plist配置hasAlwaysKey,则可以调用始终允许API

                [manager requestWhenInUseAuthorization];

            }

            else

            {

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

                NSLocationAlwaysUsageDescription或者NSLocationWhenInUseUsageDescription字段。");

            }

        }

    }

}

```

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

```objectivec

//核实当前权限状态,判断是否需要申请权限或者权限升级

- (void)checkLocationAuthorizationStatus:(CLLocationManager *)manager

                         needFullAccuracy:(BOOL)isNeedFullAccuracy

                                purposeKey:(NSString *)purposeKey{

      if([self authorizationStatus] == kCLAuthorizationStatusNotDetermined){

          //如果没有定位权限,则需要先申请定位权限

          //如果是iOS14申请权限弹窗时可以选择精度开关,所以不用在单独处理精度权限

          [self requestLocationAuthorizationIfNeed:manager];

      }else if(isNeedFullAccuracy){

          //如果已经有定位权限且需要精确定位

            [self requestTemporaryFullAccuracyAuthorizationIfNeed:manager purposeKey:purposeKey];

      }

}

//如果当前场景需要精确定位,则可以申请一次临时精确定位

- (void)requestTemporaryFullAccuracyAuthorizationIfNeed:(CLLocationManager *)manager

                                             purposeKey:(NSString *)purposeKey

{

    //如果是非iOS14系统,则默认为精确定位

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

        //如果已经获得定位权限,但精度权限只是模糊定位

        if (manager.accuracyAuthorization == CLAccuracyAuthorizationReducedAccuracy) {

            NSDictionary *locationTemporaryDictionary = [[NSBundle mainBundle]

                        objectForInfoDictionaryKey:@"NSLocationTemporaryUsageDescriptionDictionary"];

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

            if (hasLocationTemporaryKey) {

                //此API不能用于申请定位权限,只能用于从模糊定位升级为精确定位;申请定位权限只能调用

                //requestWhen或requestAlways,如果没有获得定位权限,直接调用此API无效。

                [manager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purposeKey completion:nil];

            }else{

                NSLog(@"如果需要使用临时精确定位,需要在Info.plist中添加 \

                NSLocationTemporaryUsageDescriptionDictionary字段。");

            }

        }

    }

 }

//请求定位权限,

- (void)requestLocationAuthorizationIfNeed:(CLLocationManager *)manager

{

    //系统版本号

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

    //系统版本8+ && 没有选择过定位权限

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

    {

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

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

        if (hasWhenInUseKey)

        {

            //如果plist配置InUseKey,则只能调用使用时API

            [manager requestWhenInUseAuthorization];

        }

        else{

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

            NSLocationWhenInUseUsageDescription字段。");

        }

    }

}

```

#### 5、定位权限状态变更

```objectivec

//iOS13及以前版本回调

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

{

    [self locationStatusDidChanged:[CLLocationManager authorizationStatus]];

}

//iOS14及以后版本回调

- (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: 该场景需要精确定位才可以使用,请去设置中打开精确定位开关");

            }

        }

    } else {

    }

}

- (void)locationStatusDidChanged:(CLAuthorizationStatus)authStatus

{

    switch (authStatus) {

        case kCLAuthorizationStatusNotDetermined:

            NSLog(@"可以申请定位权限");

            break;

        case kCLAuthorizationStatusRestricted:

        case kCLAuthorizationStatusDenied:

            NSLog(@"TODO: 没有定位权限");

            break;

        case kCLAuthorizationStatusAuthorizedAlways:

        case kCLAuthorizationStatusAuthorizedWhenInUse:

            NSLog(@"TODO: 拥有定位权限");

        default:

            break;

    }

}

```

#### 6、定位回调

```objectivec

//定位回调

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

{

    CLLocation *locationg = locations.lastObject;

    NSLog(@"TODO: 收到定位数据:%@",locationg);

}

```

### 五、小结

定位信息作为用户非常在意的隐私数据,iOS开发者应尽量遵循适用原则(即能满足需求的最小权限)去获取用户定位信息。本文对iOS系统定位权限说明从8.0到14.0,其中关于API调用实践是对应的最大定位权限,开发者可以根据需求参考相对应的部分。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350