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

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

我的目标是这样的:

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

我的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 中不是必需的/不建议使用。

标签: ios swift location cllocationmanager


【解决方案1】:

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

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

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

  3. 确保您的代码库中其他地方没有挥之不去的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
    猜你喜欢
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 2011-10-02
    • 2018-03-02
    • 1970-01-01
    • 2016-06-16
    • 2015-01-25
    • 2018-12-24
    相关资源
    最近更新 更多