调用requestAuthorizationWhenInUse()后LocationManager仍卡在.notDetermined状态
我正在尝试用Swift、SwiftUI和CoreLocation开发第一个应用,以下是我的代码:
LocationManager 可观察类
import Foundation import CoreLocation import SwiftUI @Observable class LocationManager: NSObject, CLLocationManagerDelegate { let manager: CLLocationManager var lastLocation: CLLocation? { manager.location } override init() { manager = CLLocationManager() super.init() manager.delegate = self manager.desiredAccuracy = kCLLocationAccuracyBest } } extension LocationManager { private func redirectToSettings() { if let url = URL(string: UIApplication.openSettingsURLString) { UIApplication.shared.open(url) } } func requestWhenInUseAuthorization() { return manager.requestWhenInUseAuthorization() } private func handleAuthorizationStatusChange(_ status: CLAuthorizationStatus) { switch status { case .notDetermined: print("not determined") requestWhenInUseAuthorization() case .authorizedWhenInUse: print("yes") manager.requestAlwaysAuthorization() return manager.startUpdatingLocation() case .authorizedAlways: return manager.startUpdatingLocation() case .denied, .restricted: return redirectToSettings() @unknown default: print("Unknown authorization status") } } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print("\(error)") } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { handleAuthorizationStatusChange(status) } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { print(locations) } }
FirstView 视图代码
import SwiftUI struct FirstView: View { @Environment(LocationManager.self) var locationManager var body: some View { if let clLocation = locationManager.lastLocation { Text(clLocation.description) } else { Text("No location") } } } #Preview { FirstView() }
ContentView 代码
struct ContentView: View { var body: some View { TabView { FirstView() .tabItem { Label("Explore", systemImage: "globe") } SecondView() .tabItem { Label("Tortoise", systemImage: "tortoise") } ThirdView() .tabItem { Label("Tortoise", systemImage: "tortoise") } } } } #Preview { ContentView() }
遇到的问题
授权回调触发时,状态是.notDetermined,于是调用requestWhenInUseAuthorization()请求授权,但用户没有收到授权弹窗,状态也没变化,回调不会再次触发。设备设置里的定位服务已经开启,请问可能是什么原因?
Info.plist 缺少权限描述
CoreLocation 请求授权时,必须在Info.plist中添加对应的权限说明,否则系统不会弹出授权弹窗。
若请求"When In Use"授权,需添加NSLocationWhenInUseUsageDescription(字符串类型),填写向用户说明权限用途的文案,比如"需要获取你的位置来提供相关服务"。
如果后续要请求"Always"授权,还需添加NSLocationAlwaysAndWhenInUseUsageDescription(iOS 11及以上)。未主动触发授权请求
当前LocationManager初始化后仅设置了代理,未主动发起授权请求。虽然didChangeAuthorization回调首次触发时会调用授权方法,但如果系统因缺少权限描述等原因不响应,就会卡住。可以在视图加载时主动触发,比如给FirstView添加onAppear:struct FirstView: View { @Environment(LocationManager.self) var locationManager var body: some View { // ... 原有视图代码 .onAppear { locationManager.requestWhenInUseAuthorization() } } }同时要确保在App入口注入
LocationManager环境对象:@main struct YourApp: App { var body: some Scene { WindowGroup { ContentView() .environment(LocationManager()) } } }授权逻辑的循环误区
当状态为.notDetermined时调用requestWhenInUseAuthorization(),这个操作本身不会再次触发didChangeAuthorization回调——只有用户在授权弹窗中做出选择后,回调才会再次触发。如果弹窗因为权限描述缺失不出现,状态就会一直停留在.notDetermined,导致流程卡住。所以优先解决权限描述问题是关键。预览环境的限制
FirstView的预览中没有注入LocationManager环境对象,预览时无法正常测试授权流程,但真机或模拟器运行时的问题与预览无关,只需确保正式运行时环境对象已正确注入即可。
内容的提问来源于stack exchange,提问作者explosivemango

