如何在iOS应用中强制获取“始终”位置访问权限

5
我正在构建的应用需要始终访问位置以正常工作。该应用程序基本上会跟踪位置并在地图上标记等(细节不重要,哈哈)。
我的目标是:
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 函数,但这并没有发生!为什么不会发生呢?相反,promptUserToChangeLocationSettings() 在用户得到询问是否启用“始终”位置访问的小弹窗之前运行,并使应用程序无法使用。

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


我会采取不同的方法。首先询问“在使用时”,然后显示一条信息,解释为什么需要“始终允许”,然后当他们关闭该屏幕时,再请求“始终允许”。您还应仔细考虑是否“在使用时”与后台位置足以满足需求,并且您还需要考虑iOS 14中不精确位置的影响。 - Paulw11
两个选项:1. 您可以请求“使用时”权限,授权后,再在授权状态更改的委托方法中请求“始终”权限。这将立即触发第二个提示(但仅适用于iOS 13.4及更高版本;例如,在13.0中,您不会看到此第二个警报)。2. 请求“始终”权限,获取临时“始终”权限(如果授权为“始终”,则使用“使用时”权限查看应用程序的授权状态)。然后,当应用程序实际行使其“始终权限”时,用户将在操作系统选择的时间看到升级为“始终”的第二个提示。这些是iOS 13.4及更高版本中的两个选项。 - Rob
@Rob,选项1不是他们已经在做的吗?也许他们没有在iOS 13.4或更高版本上进行测试。 - Paulw11
我只是想概述一下选项(并明确iOS版本要求)。 - Rob
顺便说一下,kuinsiho,在Swift中不需要/建议使用breakreturn语句以及分号。 - Rob
显示剩余3条评论
2个回答

4
关于我们先请求“仅使用时”权限,然后再请求“始终允许”权限的流程(如WWDC 2020 位置更新中所讨论的),有一些观察结果:
  1. 请确保在设备上运行此代码,而非模拟器。如果使用模拟器,您可能看不到后续的“将‘仅使用时’升级为‘始终允许’”权限提示。

  2. 此功能是在iOS 13.4中引入的。请确保您不是在早期的iOS 13版本上尝试此操作。在这些早期版本中,您将看不到升级为“始终允许”的第二个提示。

  3. 请确保您的代码库中没有任何悬挂的requestAlwaysAuthorization,因为它可能会将应用程序置于“临时始终允许”状态。一旦进入临时状态,您就无法脱离13.0的临时流程。


我知道这不是你想要的,但是为了未来的读者着想,上述的替代方法是更简单的“始终提供”流程,它在iOS 13.0中引入(在WWDC 2019的Core Location新功能中概述)。你只需要调用requestAlwaysAuthorization(从不调用requestWhenInUseAuthorization)。苹果的意图是让用户更好地理解正在发生的事情,在应用程序使用时显示“在使用时”警报,并在应用程序没有运行时使用位置服务自动显示“始终”升级警报。

1
模拟器现在支持iOS 16,并能显示“仅使用应用期间”和“始终允许”后续请求。 - undefined

1
这是一个得到我想要的结果的解决方案: 首先,在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变量? - Rob
@Rob - 我的位置管理器是全局的(在AppDelegate中声明为var locationManager:CLLocationManager,在任何函数之外),然后使用CLLocationManager.init()进行初始化。完全没有意识到这可能是问题的原因! - kiunsiho
1
全局常量和变量总是惰性计算,类似于惰性存储属性。与惰性存储属性不同的是,全局常量和变量不需要标记为lazy修饰符。 - Rob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接