如何在 iOS 应用中强制访问 'always' 位置信息

How to force 'always' location access in iOS app

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

我的目标是:

  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() 是一个正常工作的函数,它将用户带到我的应用程序的设置页面。

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

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

关于此流程的一些观察,我们首先请求“使用时”,当获得批准后,才请求“始终”(如 WWDC 2020 What's New in Location 中所述):

  1. 确保您 运行 这是在设备上,而不是模拟器上。使用模拟器时,您可能看不到随后的“将‘when-in-use’升级为‘始终’”权限警报。

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

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


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

这是一个得到我想要的结果的解决方案: 首先:在 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
    }
}