美文网首页iOS地图知识大全iOS常用
iOS定位权限与使用阐述 (含iOS14定位适配)

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

作者: 谢见章 | 来源:发表于2021-02-09 10:39 被阅读0次

    ### 一、前言

        大数据时代,用户对自己的隐私安全越来越关注,所以,随着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调用实践是对应的最大定位权限,开发者可以根据需求参考相对应的部分。

    相关文章

      网友评论

        本文标题:iOS定位权限与使用阐述 (含iOS14定位适配)

        本文链接:https://www.haomeiwen.com/subject/cjiqxltx.html