如何在 iOS 应用程序中强制“始终”访问位置

Posted

技术标签:

【中文标题】如何在 iOS 应用程序中强制“始终”访问位置【英文标题】:How to force 'always' location access in iOS app 【发布时间】:2020-12-11 08:08:09 【问题描述】:

我正在构建的应用始终需要位置访问权限才能正常工作。该应用程序基本上会跟踪位置并将其放在地图和其他东西上(细节不重要,哈哈)。

我的目标是这样的:

    提示用户启用“始终”位置访问 如果始终请求位置访问但用户拒绝,则使应用无法使用 - 基本上只需显示一个小按钮,将他们重定向到可以更改该设置的设置。

我的AppDelegate.swift是在实现CLLocationManagerDelegate,代码如下:

alreadyRequestedLocationWhenInUse = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationWhenInUse")
alreadyRequestedLocationAlways = UserDefaults.standard.bool(forKey: "alreadyRequestedLocationAlways")

        func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) 
      
        switch CLLocationManager.authorizationStatus() 
        case .notDetermined:
            if (!alreadyRequestedLocationWhenInUse) 
                print("Requesting location access 'while in use'.")
                self.locationManager.requestWhenInUseAuthorization();
                UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationWhenInUse")
                alreadyRequestedLocationWhenInUse = true
             else 
                promptToChangeLocationSettings()
            
        case .restricted, .denied:
            print("No Location access")
            promptToChangeLocationSettings()
            break;
        case .authorizedWhenInUse:
            if (!alreadyRequestedLocationAlways) 
                print("Requesting location access 'Always'.")
                self.locationManager.requestAlwaysAuthorization()
                UserDefaults.standard.set(true, forKey: "alreadyRequestedLocationAlways")
                alreadyRequestedLocationAlways = true
             else 
                promptToChangeLocationSettings()
            
            break;
        case .authorizedAlways:
            self.startLocationMonitoring();
            break;
        default:
            self.locationManager.requestAlwaysAuthorization();
            return
        
    

promptToChangeLocationSettings() 是一个正常工作的函数,它将用户带到我的应用程序的设置页面。

问题在于,在用户退出应用并返回之前,系统不会提示用户启用“始终位置跟踪”。他们被要求提供“使用时”权限(我知道它的工作方式是他们必须先同意),但我希望总是提示立即发生!理论上,locationManagerDidChangeAuthorization 函数应该在“while use”授权被授予后再次调用,但这不会发生!为什么这不会发生?取而代之的是,promptUserToChangeLocationSettings() 在用户收到询问他们是否要启用“始终”位置访问的小弹出窗口之前运行并使应用程序无法使用。

有人可以帮我解决这个问题吗? 顺便说一句,我正在使用 UserDefaults 来跟踪我们是否完成了位置许可请求(因为请求只能完成一次)。

【问题讨论】:

我会采用不同的方法。询问“使用时”,然后显示解释为什么需要“始终”的信息,然后当他们关闭该屏幕时,询问始终。您还应该仔细考虑“使用时”与背景位置是否足够,您还需要考虑 ios 14 中位置不精确的影响。 两个选项: 1.你可以在使用时请求,在他们授予它之后,然后总是在授权状态更改委托方法中请求。这将立即触发第二个提示(但仅在 iOS 13.4 及更高版本中;例如,在 13.0 中,您不会看到第二个提示)。 2. 总是请求,总是得到临时(使用时看到使用时,授权状态如应用程序看到的授权状态)。然后,当应用程序实际运行它的“始终功能”时,用户将在操作系统选择的那个时间看到第二个提示,要求升级到始终。这是 iOS 13.4 及更高版本中的两个选项。 @Rob,选项 1 不是他们已经在做的吗?也许他们没有在 iOS 13.4 或更高版本上进行测试。 我只是想概述选项(并明确说明 iOS 版本要求)。 顺便说一下,kuinsiho、breakreturn 语句和分号在 Swift 中不是必需的/不建议使用。 【参考方案1】:

关于这个流程的一些观察,我们首先请求“使用时”,当它被授予时,才请求“始终”(如 WWDC 2020 What's New in Location 中所讨论的):

    确保您在设备上运行它,而不是在模拟器上。使用模拟器时,您可能看不到后续“将‘使用时’升级为‘始终’”权限提醒。

    此功能是在 iOS 13.4 中引入的。确保您没有在较早的 iOS 13 版本上尝试此操作。在这些早期版本中,您不会看到升级到“始终”的第二个警报。

    确保您的代码库中其他地方没有挥之不去的requestAlwaysAuthorization,这可能会使应用程序处于“始终临时”状态。一旦处于临时状态,您将被锁定在 13.0 的临时流程中。


我知道这不是您想要的,但为了未来的读者,上面的替代方案是 iOS 13.0 中引入的更简单的“临时始终”流程(在 WWDC 2019 的 What's New in Core Location 中进行了概述) .您只需致电requestAlwaysAuthorization(从不致电requestWhenInUseAuthorization)。 Apple 的意图是让用户更好地了解正在发生的事情,在应用程序使用时显示“使用时”警报,并在应用程序未运行时使用定位服务时自动显示“始终”升级警报.

【讨论】:

【参考方案2】:

这是一个得到我想要的结果的解决方案: 首先:在 AppDelegate.swift didFinishLaunchingWithOptions 函数中调用 locationManagerDidChangeAuthorization(locationManager)。我还在 applicationWillEnterForeground 中调用了它,这样每次应用打开时它都会重新检查。

其次,这是我的新 locationManagerDidChangeAuthorization 函数。只需要删除 return/break 语句,但我现在要在我忘记之前回答这个问题:

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) 
  
    switch CLLocationManager.authorizationStatus() 
    case .notDetermined:
        UserDefaults.standard.set(false, forKey: "alreadyRequestedAlwaysLocationAccess")
        alreadyRequestedAlwaysLocationAccess = false
        DispatchQueue.main.async
            self.coreLocationManager.requestWhenInUseAuthorization()
            self.locationManagerDidChangeAuthorization(manager)
        
        break;
    case .restricted, .denied:
        print("No Location access")
        promptToChangeLocationSettings()
        break;
    case .authorizedWhenInUse:
        if (!alreadyRequestedAlwaysLocationAccess) 
            print("Requesting location access 'Always'.")
            UserDefaults.standard.set(true, forKey: "alreadyRequestedAlwaysLocationAccess")
            alreadyRequestedAlwaysLocationAccess = true

            DispatchQueue.main.async
                self.coreLocationManager.requestAlwaysAuthorization()
                self.locationManagerDidChangeAuthorization(manager)
            
         else 
            promptToChangeLocationSettings()
        
        break;
    case .authorizedAlways:
        self.startLocationMonitoring();
        break;
    default:
        return
    

【讨论】:

很高兴你解决了这个问题。不过,通常您不必手动调用它。当您设置委托时,它应该被自动调用。也许您的位置管理器是全局的或静态的(两者都懒惰地实例化它)或明确地将其标记为 lazy var? @Rob - 我的位置管理器是全局的(在 AppDelegate 中声明为 var locationManager : CLLocationManager,在任何函数之外),然后使用 CLLocationManager.init() 进行初始化。完全没有意识到这可能是问题的原因! “全局常量和变量总是延迟计算,类似于延迟存储属性。与惰性存储属性不同,全局常量和变量不需要使用惰性修饰符进行标记。” docs.swift.org/swift-book/LanguageGuide/Properties.html#ID263

以上是关于如何在 iOS 应用程序中强制“始终”访问位置的主要内容,如果未能解决你的问题,请参考以下文章

iOS 11 中的位置访问请求

有啥方法可以强制在 iOS13 的入职过程中出现“始终允许”提示?

关于 iPhone 方向和固件:始终强制方向

IOS触发位置权限弹窗

iOS 13 中“始终允许”位置的问题

每次应用启动时如何强制phonegap询问地理位置?