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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:30:48