如何在locationManagerDidChangeAuthorization调用时触发SearchManagerViewModel方法
解决位置权限变更时自动更新城市名的问题
你的核心问题是:当用户在视图内切换位置服务权限时,SearchManagerViewModel没有感知到权限变化,无法自动触发城市搜索更新。下面是具体的解决方案,分步骤优化你的代码:
步骤1:给LocationManager添加权限状态的可监听属性
我们需要让LocationManager对外暴露权限状态的变化,这样上层的ViewModel可以感知到并做出响应:
final class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate { private let locationManager = CLLocationManager() @Published var lastSearchedCity = "" @Published var authorizationStatus: CLAuthorizationStatus = .notDetermined // 新增:对外暴露权限状态 var hasFoundOnePlacemark: Bool = false func checkIfLocationServicesIsEnabled(){ DispatchQueue.global().async { if CLLocationManager.locationServicesEnabled(){ self.locationManager.delegate = self self.locationManager.desiredAccuracy = kCLLocationAccuracyBest self.checkLocationAuthorization() }else{ // 提示用户:位置服务已关闭 } } } private func checkLocationAuthorization(){ let status = locationManager.authorizationStatus self.authorizationStatus = status // 更新权限状态 switch status{ case .notDetermined: locationManager.requestWhenInUseAuthorization() case .restricted, .denied: // 提示用户:权限被限制/拒绝 self.lastSearchedCity = "" // 权限失效时清空城市名 case .authorizedWhenInUse, .authorizedAlways: locationManager.startUpdatingLocation() default: break } } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { checkLocationAuthorization() } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { hasFoundOnePlacemark = false guard let location = manager.location else { self.locationManager.stopUpdatingLocation() return } CLGeocoder().reverseGeocodeLocation(location) { [weak self] placemarks, error in guard let self = self else { return } self.locationManager.stopUpdatingLocation() if let error = error { // 处理逆地理编码错误 return } guard let placemark = placemarks?.first else { // 提示用户:未找到位置信息 return } self.lastSearchedCity = placemark.locality ?? "" } } }
步骤2:让SearchManagerViewModel自动监听权限和城市变化
修改ViewModel,使其自动监听LocationManager的权限变化和城市名更新,无需手动调用searchAndSetCity():
class SearchManagerViewModel: ObservableObject{ @Published var cityName = "" private let locationManager = LocationManager() init() { // 自动同步LocationManager的城市名到ViewModel的cityName _ = locationManager.$lastSearchedCity .receive(on: DispatchQueue.main) .assign(to: &$cityName) // 监听权限状态变化,权限恢复时自动触发位置搜索 _ = locationManager.$authorizationStatus .receive(on: DispatchQueue.main) .sink { [weak self] status in guard let self = self else { return } switch status { case .authorizedWhenInUse, .authorizedAlways: self.locationManager.checkIfLocationServicesIsEnabled() case .denied, .restricted: self.cityName = "" // 权限失效时清空显示 default: break } } // 初始化时自动检查一次位置服务 locationManager.checkIfLocationServicesIsEnabled() } // 保留手动触发方法(如果需要) func searchAndSetCity(){ locationManager.checkIfLocationServicesIsEnabled() } }
步骤3:简化ContentView
现在视图无需在onAppear中手动调用方法,ViewModel会自动处理所有更新逻辑:
struct ContentView: View { @StateObject private var searchManager = SearchManagerViewModel() var body: some View { VStack { Text(searchManager.cityName) .font(.callout) } } }
为什么这个方案有效?
- 权限变化感知:当用户切换位置服务权限时,
LocationManager的locationManagerDidChangeAuthorization会被触发,更新authorizationStatus属性。SearchManagerViewModel通过监听这个属性,在权限恢复为授权状态时自动重新触发位置搜索。 - 实时城市同步:
SearchManagerViewModel监听LocationManager的lastSearchedCity变化,自动同步到cityName,确保视图实时更新。 - 内存安全:使用
weak self避免循环引用,同时优化了逆地理编码的可选绑定逻辑,提升代码稳定性。
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

