一、概述
由于最近在开发跑步相关的功能,涉及到地图定位功能,因此需要申请定位权限,之前APP中有定位需要,但因之前未细致的研究授权模式的配置,导致在开发跑步功能时产生了从未遇到的异常问题。
二、遇到的问题
- 问题描述:
问题描述:首次安装APP,授权定位权限为
WhenInUse
使用期间定位定位,然后开启跑步,此时接收定位回调,将APP切入后台等待3-5秒钟后,发现定位回调不再继续回调给APP,此时将APP返回前台,将恢复定位回调,可接收到定位结果数据。然后再将APP切回后台,这时会再次弹出定位模式选择弹窗,内容为”保持仅使用期间“和”更改为始终允许“。
-
选择”保持仅使用期间“:
会报错误
定位发生错误Error Domain=com.baidu.location.locationerrordomain Code=2
"手机不允许定位,请确认用户授予定位权限或者手机是否打开定位开关"
UserInfo={NSLocalizedDescription=手机不允许定位,
请确认用户授予定位权限或者手机是否打开定位开关}
注:此时定位模式被切换到
whenInUse使用期间定位模式,前后台定位功能可正常使用
-
选择”更改为始终允许“:
此时定位模式被切换到Always
始终定位模式,前后台定位功能可正常使用
- 问题的思考:
经过对代码实现的调查发现,启动时申请定位权限时使用的requestAlwaysAuthorization
,而不是requestWhenInUseAuthorization
,虽然两者弹出的系统弹窗相同,但是使用流程上是有区别的。因为我们的APP之前使用了Always
申请始终定位权限,虽然在首次弹窗是选择了”使用APP时允许“但是我们在打印授权状态时是kCLAuthorizationStatusAuthorizedAlways
始终定位状态,这和我们选择的”使用APP时允许“初衷有所不同。那如何正确申请定位模式呢?
三、定位基础
- locationServicesEnabled
系统全局定位开关,确定用户是否启用了位置服务,如果返回NO,需要提示用户到设置隐私中开启定位服务。
- @property (nonatomic, readonly) CLAuthorizationStatus authorizationStatus
返回应用程序当前定位授权状态
typedef NS_ENUM(int, CLAuthorizationStatus) {
kCLAuthorizationStatusNotDetermined = 0, // 用户未授权,即还未弹出OS的授权弹窗
kCLAuthorizationStatusDenied, // 用户拒绝定位权限,包括拒绝App或者全局开关关闭
kCLAuthorizationStatusRestricted, // 定位服务受限,该状态位用户无法通过设置页面进行改变
kCLAuthorizationStatusAuthorizedAlways, // 始终定位,即后台定位
kCLAuthorizationStatusAuthorizedWhenInUse, // App使用的时候,允许定位
kCLAuthorizationStatusAuthorized, // iOS8.0之后已经被废弃
};
- @property (nonatomic, readonly) CLAccuracyAuthorization accuracyAuthorization
返回应用程序当前定位的精确度
typedef NS_ENUM(NSInteger, CLAccuracyAuthorization) {
CLAccuracyAuthorizationFullAccuracy, //精准定位
CLAccuracyAuthorizationReducedAccuracy, // 模糊定位
};
- @property(weak, nonatomic, nullable) id<CLLocationManagerDelegate> delegate;
定位的代理实例,定位状态的变化会在CLLocationManagerDelegate的方法中回调
- @property(assign, nonatomic) BOOL pausesLocationUpdatesAutomatically
指定定位是否会被系统自动暂停,默认是YES
- @property(assign, nonatomic) BOOL allowsBackgroundLocationUpdates
是否允许后台定位,默认是NO
- requestWhenInUseAuthorization
申请使用期间定位模式,下面会详细解释
- (void)requestAlwaysAuthorization
申请始终定位模式,下面会详细解释
四、使用期间定位模式
- 需要在Info.plist中配置
NSLocationWhenInUseUsageDescription
; - 调用方法
requestWhenInUseAuthorization
申请使用期间定位模式,必须在status = kCLAuthorizationStatusNotDetermined
的时候,调用才会出现系统弹窗,否则无响应; - APP必须在前台运行时才会显示系统弹窗
- 系统弹窗选项:
选项 | 授权 |
---|---|
使用App时允许 | APP使用时授权,不会失效status = WhenInUse |
允许一次 | 临时授权,授权后status = WhenInUse ,下次再次启动App时授权会失效 status = notDetermined |
不允许 | 拒绝授权,不允许授权请求status = Denied |
- 当应用程序在前台开启正常使用时定位权限后,如果在Xcode中开启后台定位刷新的能力,定位服务仍然可以在后台继续运行;
- 当应用程序切换到具有后台位置更新服务的后台时,系统会在状态栏中显示蓝色位置服务指示器,表示定位仍在进行中;
在iOS 16及更高版本中,主动跟踪用户位置或最近启用核心位置的应用程序会在Control Center中显示一个指示器。
- 之后如果还需要定位,则需要自己弹窗提醒用户。
五、始终定位模式
- 需要在Info.plist中配置
NSLocationAlwaysAndWhenInUseUsageDescription
、NSLocationWhenInUseUsageDescription
如果需要支持 iOS10 的话需要配置NSLocationAlawaysUsageDescription
; - 可以在授权状态为
notDetermined
和whenInUse
时可调用requestAlwaysAuthorization
; - 系统对
requestAlwaysAuthorization
方法的调用是有一定的限制的,当APP调用此方法后,在继续进一步调用是无响应的,也就是说不可以连续调用; - 授权方式
A、 先获取"使用期间定位"模式后,再申请"始终定位"权限
获取Always
权限前应用程序需要先获取到whenInUse
权限,然后再请求Always
授权。
1、如果APP在获取whenInUse
权限后,立即调用requestAlwaysAuthorization方法,系统会立即提示用户是否授权Always
权限或保持whenInUse
权限。
2、如果用户选择”仅允许一次“授权后,则系统会忽略requestAlwaysAuthorization
方法的任何调用,此时属于临时授权状态。
选项 | 授权 |
---|---|
保持仅使用期间 | 定位权限将继续保持为使用期间定位,代理未收到任何回调status = WhenInUse |
更改为始终允许 | 定位权限将修改为始终定位,代理会收到状态变化回调status = authorizedAlways |
当 status = notdetermined时,调用requestWhenInUseAuthorization,只有用户同意“使用App时允许”的情况下才有用,然后当 status = WhenInUse时,调用requestAlwaysAuthorization或系统查觉到APP需要”始终“定位时,会再次出现始终授权模式弹窗。
B、直接申请获取"始终定位"权限
如果APP在定位授权状态为notDetermined
时,此时调用requestAlwaysAuthorization
方法后,系统会出现两次弹窗。
1、第一次弹窗与申请”使用期间定位“权限时相同;但是选择”使用App时允许“意义有所不同
选项 | 授权 |
---|---|
使用App时允许 | APP获得临时的”始终“定位权限,status = authorizedAlways |
允许一次 | 临时授权,授权后status = WhenInUse ,下次再次启动App时授权会失效 status = notDetermined |
不允许 | 拒绝授权,不允许授权请求status = Denied |
2、第二次弹窗的时机:
- 当系统准备向APP传递的事件需要authorizedAlways权限时;
- 如果APP处于临时”始终“定位状态时;
- 当APP进入后台进入非活跃状态挂起时;
选项 | 授权 |
---|---|
保持仅使用期间 | 系统将授权由临时”始终“更改为”使用期间“,代理会收到状态变化事件,status = WhenInUse |
更改为始终允许 | 移除临时”始终”定位权限,变为“始终”授权状态,代理不会会收到状态变化回调,status = authorizedAlways |
注:如果用户在提示出现后做出选择,并选择允许“始终”权限,则位置事件将发送到您的应用程序。
六、确定应用需要的授权限
A、应用程序的定位授权状态决定了其是否需要及何时接收定位事件:
- When In Use(APP使用期间定位)
您的应用程序可以在使用中使用所有位置服务并接收事件。一般来说,如果iOS应用程序位于前台或在后台运行,并且后台位置使用指示器已启用,则视为正在使用。 - Always(APP总是定位)
您的应用程序可以使用所有位置服务并接收事件,即使用户不知道您的应用程序正在运行。如果你的应用程序未运行,系统将启动你的应用程序并传递事件。 - “使用期间定位”是首选模式
B、尽可能的请求授权使用“使用期间定位”模式,此模式具有强大的功能,允许您的应用程序:
1、在用户使用应用程序时访问所有可用的位置服务。如果用户停止使用你的应用程序,任何未完成的请求都将挂起,直到用户恢复使用你的应用程序为止,才会恢复定位服务
2、如果您在Xcode项目中启用了后台位置更新,即使在应用程序进入后台后,也可以继续获取位置更新。
3、使用位置通知触发器启动。如果您的应用程序可以依赖用户的交互,请设置UNLocationNotificationTrigger
,以在用户进入相关区域时通知用户。当用户点击通知时,系统启动应用程序,使其有资格接收位置事件。此方法允许用户在相关时刻决定是否与您的应用程序共享其位置。
C、当应用程序授权“使用期间定位”时,如何确定应用程序正在使用后定位?
1、当应用程序在前台运行时;
2、在应用程序离开前台后进入后的几秒钟内,您的应用程序将有一个短的宽时间限期来完成用户启动的任何当前位置任务;
3、当应用程序显示后台位置使用指示器(showsBackgroundLocationIndicator
)时。在iOS上,指示灯是屏幕顶部的蓝色条或小球;
D、请求"始终"授权:
在以下情况下,您可能需要请求始终授权:
1、您的应用程序执行自动任务,在此期间可能会不方便或不需要的显示提示;
2、你的应用程序记录了一天中的许多位置,例如日记应用程序。用户可能更倾向于允许始终授权,以便您的应用程序即使在未使用时也可以记录位置,而无需提示用户;
请记住,请求授权并不能保证你的应用程序会收到授权。如果您请求“始终定位”授权,则用户可以选择在使用时授予您的应用程序授权。您必须始终准备好在使用授权时运行。
注:如果您的应用程序已经授权“使用时定位”授权,您可以稍后单独请求
Always authorization。然而,应用程序可能只发出一个始终授权请求。
七、结束语
经过分析和验证试验,确定在我的APP中定位回调异常问题是因为请求授权方式使用错误,优先使用When In Use
定位模式,然后在需要使用Always”
定位权限的位置再申请权限。当APP得到定位模式被用户选择为“When In Use”时,进入后台观察左上角是否有定位指示器(蓝色图标),存在的话可以确定当前定位服务正常。如果选择Always
模糊,那么屏幕左上角会显示定位图标。