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