0

我正在努力在已显示的标注中获取 KVO 更新。

我的用例:我想在打开的标注上显示用户位置和我添加到地图的注释之间的实时距离。注解不会改变它的位置。

  • 我使用我定义的自定义注释向 mapView 添加注释。这里没有问题。
  • 在选择的每个注释上,标注显示自定义注释中定义的所有信息
  • 但是,只有当我取消选择注释并重新选择它时,才会在标注中刷新距离

distance 属性被声明为@objc dynamic,因此可以观察到。每次用户位置更改时,我都会计算距离。这部分也有效。

我无法弄清楚我缺少什么来更新标注而不关闭并重新打开它。

我正在使用的代码是 Rob 在此处描述的:Swift -How to Update Data in Custom MKAnnotation Callout?

所以我的问题是:是否可以在 notificationView 标注中实时更改值(观察到的)?如果是,KVO 是最好的方法吗?在下面的链接中,如何实现 mapView viewFor 方法?

任何示例都会非常有帮助。

这是我在这里的第一篇文章,所以如果我做错了,请告诉我,我会提供更多信息和细节。但我的情况是微不足道的:标准标注对标题和副标题执行键值观察 (KVO)。(并且注释视图观察坐标的变化。)。但是如何在当前打开的标注中显示值的变化?那是我不明白的想法。

CustomAnnotation 类:

class CustomAnnotation: NSObject, MKAnnotation {
    @objc dynamic var title: String?
    @objc dynamic var subtitle: String?
    @objc dynamic var coordinate: CLLocationCoordinate2D
    
    @objc dynamic var distance: CLLocationDistance
    var poiColor: String?
    var poiPhone: String?    
    
    init(title: String, subtitle: String, coordinate: CLLocationCoordinate2D, poiColor: String, poiPhone: String, distance: CLLocationDistance) {
        self.title = title
        self.subtitle = subtitle
        self.coordinate = coordinate
        self.poiColor = poiColor
        self.poiPhone = poiPhone
        self.distance = distance
        super.init()
    }
}

CustomAnnotationView 类:

    class CustomAnnotationView: MKMarkerAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        displayPriority = .required
        canShowCallout = true
        detailCalloutAccessoryView = createCallOutWithDataFrom(customAnnotation: annotation as? CustomAnnotation)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit {
        removeAnyObservers()
    }

    override var annotation: MKAnnotation? {
        didSet {
            removeAnyObservers()
            if let customAnnotation = annotation as? CustomAnnotation {
                updateAndAddObservers(for: customAnnotation)
            }
        }
    }

    private var subtitleObserver: NSKeyValueObservation?
    private var distanceObserver: NSKeyValueObservation?

    private let subtitleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    private let distanceLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
}

private extension CustomAnnotationView {
    func updateAndAddObservers(for customAnnotation: CustomAnnotation) {
        
        subtitleLabel.text = customAnnotation.subtitle
        subtitleObserver = customAnnotation.observe(\.subtitle) { [weak self] customAnnotation, _ in
            self?.subtitleLabel.text = customAnnotation.subtitle
        }
        
        let locationManager = CLLocationManager()
        let theLatitude:CLLocationDegrees = (locationManager.location?.coordinate.latitude)!
        let theLongitude:CLLocationDegrees = (locationManager.location?.coordinate.longitude)!
        // Get pin location
        let pointLocation = CLLocation(latitude: customAnnotation.coordinate.latitude, longitude: customAnnotation.coordinate.longitude)
        //Get user location
        let userLocation = CLLocation(latitude: theLatitude, longitude: theLongitude)
        // Return distance en meters
        let distanceFromUser = pointLocation.distance(from: userLocation)
        customAnnotation.distance = distanceFromUser*100
        distanceLabel.text = String(format: "%.03f", customAnnotation.distance)+" cm"
        distanceObserver = customAnnotation.observe(\.distance) { [weak self] customAnnotation, _ in
            self?.distanceLabel.text = "\(customAnnotation.distance) cm"
        }
    }

    func removeAnyObservers() {
        subtitleObserver = nil
        distanceObserver = nil
    }

    func createCallOutWithDataFrom(customAnnotation: CustomAnnotation?) -> UIView {
        
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = true
        view.addSubview(subtitleLabel)
        view.addSubview(distanceLabel)
        
        NSLayoutConstraint.activate([
            subtitleLabel.topAnchor.constraint(equalTo: view.topAnchor),
            subtitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            subtitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            subtitleLabel.bottomAnchor.constraint(equalTo: distanceLabel.topAnchor),
            distanceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            distanceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            distanceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])

        if let customAnnotation = customAnnotation {
            updateAndAddObservers(for: customAnnotation)
        }
return view
    }
}

并完成:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    if annotation is MKUserLocation { return nil }
    let annotation = annotation as? CustomAnnotation
    
    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "CustomAnnotation") as? CustomAnnotationView
    
    if annotationView == nil {
        annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: "CustomAnnotation")
        annotationView?.canShowCallout = true
    } else {
        annotationView?.annotation = annotation
    }
    return annotationView
}

谢谢你。

4

1 回答 1

0

您似乎已经为subtitleand正确配置了观察者distance。问题是位置的更改不会触发对distance. 因此,没有任何东西触发 KVO。

你有一个观察者distance,它将触发标签的更新。但你没有改变distance。您应该CLLocationManager从添加观察者的例程中删除代码,而是创建一个位置管理器(但不在注释视图中),它使用它delegate来更新所有注释距离,例如:

class ViewController: UIViewController {
    @IBOutlet weak var mapView: MKMapView!

    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.distanceFilter = 5
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.delegate = self
        locationManager.requestWhenInUseAuthorization()
        locationManager.startUpdatingLocation()
    }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let currentLocation = locations.last(where: { $0.horizontalAccuracy >= 0 }) else { return }

        mapView.annotations
            .compactMap { $0 as? CustomAnnotation }
            .forEach {
                $0.distance = CLLocation(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude)
                    .distance(from: currentLocation)
            }
    }
}

显然,您CLLocationManager将从updateAndAddObservers.

于 2021-08-02T22:51:13.857 回答