如何在Swift类中监听@Published变量lastSeenLocation变化以检测位置
实现持续位置半径检测的解决方案
原代码核心问题
你的LocationViewModel缺少CLLocationManagerDelegate的核心方法locationManager(_:didUpdateLocations:),导致lastSeenLocation永远不会被赋值更新。另外,要实现持续检测用户位置是否在目标区域内,有两种可行方案:在ViewModel内部处理检测逻辑,或者在View层监听位置变化触发检测。
方案一:在ViewModel内部整合检测逻辑
直接完善ViewModel,添加位置更新代理方法,每次位置更新时自动执行检测:
import CoreLocation import Combine class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate { @Published var lastSeenLocation: CLLocation? @Published var currentPlacemark: CLPlacemark? @Published var authorizationStatus: CLAuthorizationStatus // 新增:对外发布是否在目标区域内的状态 @Published var isWithinVenueRadius = false private let locationManager: CLLocationManager // 目标地点固定坐标 private let venueLocation = CLLocation(latitude: 51.500909, longitude: -0.177366) private let radius: Double = 5 // 检测半径(英里) override init() { locationManager = CLLocationManager() authorizationStatus = locationManager.authorizationStatus super.init() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest // 可选:设置距离过滤,比如移动100米才更新位置,减少耗电 locationManager.distanceFilter = 100 locationManager.startUpdatingLocation() } func requestPermission() { locationManager.requestAlwaysAuthorization() } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus // 权限变更后如果允许,重新启动位置更新 if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse { locationManager.startUpdatingLocation() } } // 新增:处理位置更新的核心方法 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let latestLocation = locations.last else { return } lastSeenLocation = latestLocation // 每次位置更新时自动执行检测 checkIfWithinVenueRadius() } // 封装检测逻辑 private func checkIfWithinVenueRadius() { guard let userLocation = lastSeenLocation else { return } let distanceInMeters = userLocation.distance(from: venueLocation) let distanceInMiles = distanceInMeters * 0.00062137 isWithinVenueRadius = distanceInMiles < radius // 这里可以添加自定义逻辑,比如发送本地通知、更新业务状态等 } }
方案二:在View层监听位置变化触发检测
如果希望检测逻辑放在View层,先完善ViewModel确保lastSeenLocation能正常更新,再用SwiftUI的.onChange修饰符监听变化:
完善后的ViewModel(补充位置更新方法)
import CoreLocation class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate { @Published var lastSeenLocation: CLLocation? @Published var currentPlacemark: CLPlacemark? @Published var authorizationStatus: CLAuthorizationStatus private let locationManager: CLLocationManager override init() { locationManager = CLLocationManager() authorizationStatus = locationManager.authorizationStatus super.init() locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.startUpdatingLocation() } func requestPermission() { locationManager.requestAlwaysAuthorization() } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { authorizationStatus = manager.authorizationStatus if authorizationStatus == .authorizedAlways || authorizationStatus == .authorizedWhenInUse { locationManager.startUpdatingLocation() } } // 必须添加此方法才能更新lastSeenLocation func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { lastSeenLocation = locations.last } }
View层代码示例
import SwiftUI import CoreLocation struct ContentView: View { @StateObject private var locationViewModel = LocationViewModel() private let venueLocation = CLLocation(latitude: 51.500909, longitude: -0.177366) private let radius: Double = 5 // 英里 var body: some View { Text(locationViewModel.lastSeenLocation == nil ? "获取位置中..." : "位置已更新") .onAppear { locationViewModel.requestPermission() } .onChange(of: locationViewModel.lastSeenLocation) { newLocation in guard let userLocation = newLocation else { return } let distanceInMeters = userLocation.distance(from: venueLocation) let distanceInMiles = distanceInMeters * 0.00062137 if distanceInMiles < radius { print("用户在目标地点5英里范围内") // 执行区域内的业务逻辑,比如显示提示、触发功能等 } else { print("用户不在目标地点5英里范围内") // 执行区域外的逻辑 } } } }
关键注意事项
- 必须在
Info.plist中添加位置权限描述:NSLocationAlwaysAndWhenInUseUsageDescription或NSLocationWhenInUseUsageDescription,否则权限请求会直接失败。 - 若不需要高频位置更新,建议设置
distanceFilter属性(如100米),减少不必要的位置回调,降低设备耗电。
内容的提问来源于stack exchange,提问作者user17864647
相关产品推荐
相关产品推荐

