You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用stopUpdatingLocation()后后台临时定位授权重置问题咨询

临时定位权限(Allow Once)返回应用后变回.notDetermined的问题解答

现象是否为预期行为?

是,这属于苹果临时定位权限的预期行为。

苹果对「Allow Once」权限的定义是:仅在当前应用会话内有效,当用户将应用切换到后台且定位任务结束后,系统会自动将授权状态重置为.notDetermined。这里的「ceases to use the app」不仅指应用被终止,还包括用户结束当前的定位使用场景——即使应用只是进入后台,只要定位任务停止,临时权限就会失效,授权状态被重置。

解决办法

  • 引导用户授予持久化权限:
    在请求权限前,通过弹窗或引导页向用户说明应用需要持续定位的场景,请求用户选择「Allow While Using App」(使用期间允许)或「Allow Always」(始终允许),这两种权限不会在后台结束定位后重置状态。可在checkRequestPermission方法中,针对临时权限过期的情况,提示用户选择持久化权限。
  • 优化权限检查逻辑:
    回到前台检查权限时,若发现状态为.notDetermined,可先判断用户是否有过临时授权记录(比如通过本地存储标记),再决定是否直接请求权限,减少对用户的打扰。

修正后的相关代码

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let scene = (scene as? UIWindowScene) else { return }
        window = UIWindow(windowScene: scene)
        window?.makeKeyAndVisible()
        window?.rootViewController = MainViewController()
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        AppDelegate.loc.checkRequestPermission()
    }
}

class MainViewController: UIViewController {  
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        
        let button = ReusableUIElements.createButton(title: "Start")
        view.addSubview(button)
        button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
        ReusableUIElements.buttonConstraints(button: button, safeArea: view.safeAreaLayoutGuide, bottomAnchorConstant: -40)
    }

    @objc func buttonPressed() {
        // 手动切后台时触发定位
        AppDelegate.loc.retrieveLocation()
    }
}

class Location: NSObject, CLLocationManagerDelegate {
    var locationManager: CLLocationManager!
    // 标记用户是否使用过临时权限
    private var hadTemporaryAuth = false

    override init() {
        super.init()
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest 
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.showsBackgroundLocationIndicator = true
    }

    func checkRequestPermission() {
        let status = locationManager.authorizationStatus
        if status == .notDetermined {
            if hadTemporaryAuth {
                // 提示用户选择持久化权限
                let alert = UIAlertController(title: "定位权限", message: "为了持续提供服务,请选择「使用期间允许」或「始终允许」", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "知道了", style: .default) { _ in
                    self.locationManager.requestWhenInUseAuthorization()
                })
                UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true)
            } else {
                locationManager.requestWhenInUseAuthorization()
            }
        } else if status == .authorizedWhenInUse || status == .authorizedAlways {
            print("已拥有持久化定位权限")
        } else {
            print("定位权限被拒绝,请前往设置开启")
        }
    }

    func retrieveLocation()  {
        locationManager.startUpdatingLocation()
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
            print("10秒后停止定位")
            self.locationManager.stopUpdatingLocation()
        }
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location) { placemarks, error in
            if let error = error {
                print("逆地理编码失败:\(error.localizedDescription)")
                return
            }
            guard let placemark = placemarks?.first else { return }
            print(placemark)
        }
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("定位失败:\(error.localizedDescription)")
    }

    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        print("授权状态变更")
        switch locationManager.authorizationStatus {
            case .denied, .restricted:
                print("权限被拒绝/受限")
            case .authorizedWhenInUse, .authorizedAlways:
                print("已授权")
            case .authorizedOnce:
                // 标记用户使用过临时权限
                hadTemporaryAuth = true
                print("临时授权")
            default:
                print("未确定")
        }
    }
}

内容的提问来源于stack exchange,提问作者Shiv Nik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:27:18