Swift - 如何更新自定义 MKAnnotation 标注中的数据?

Lan*_*ria 3 mkannotation ios swift calloutview

我的地图视图有一个自定义注释。我最初使用一些数据设置了坐标、标题(例如“第一个标题”)、副标题(例如“第一个地址”)、userId 和距离(例如 0 米)属性。我将其添加到 mapView 和数组中以供以后使用。一切正常,它显示在地图视图上,我按下它,标注显示初始数据。

后来我得知该标注的位置已更改。我循环遍历数组并使用坐标、标题(例如“新标题”)、副标题(例如“新地址”)和距离(例如 100 米)属性的新数据更新标注。我还将标注从原始位置动画化到新位置。动画工作正常,标注从 A 点移动到 B 点。

问题是,当我点击注释时,旧数据会显示在标注上,而不是新数据。

我过去常calloutAccessoryControlTapped推新的VC。当我在那里放置断点时,自定义引脚具有所有新数据。该错误似乎是在标注时发生的。

我该如何解决?

我不想清除地图视图中的所有注释,所以这不是一个选项。我致电mapView.removeAnnotation(customPin)mapView.addAnnotation(customPin)解决了该图钉的问题,但是当图钉被移除并添加回地图时,会出现闪烁,然后当它动画到新位置时,它看起来不稳定。

自定义注释

class CustomPin: NSObject, MKAnnotation {

    @objc dynamic var coordinate: CLLocationCoordinate2D
    var title: String?
    var subtitle: String?
    var userId: String?
    var distance: CLLocationDistance?

    init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, userId: String, distance: CLLocationDistance?) {

        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle
        self.userId = userId
        self.distance = distance

        super.init()
    }
}
Run Code Online (Sandbox Code Playgroud)

第一次使用初始数据设置注释

firstFunctionThatGetsTheInitialLocation(origLat, origLon) {

   let firstCoordinate = CLLocationCoordinate2DMake(origLat, origLon)   

   let distanceInMeters: CLLocationDistance = self.center.distance(from: anotherUsersLocation)

   let customPin = CustomPin(coordinate: firstCoordinate, title: "first title", subtitle: "first address", userId: "12345", distance: distance)

    DispatchQueue.main.async { [weak self] in

      self?.mapView.addAnnotation(customPin)

      self?.arrOfPins.append(customPin)
    }
}
Run Code Online (Sandbox Code Playgroud)

第二次使用新数据设置注释

secondFunctionThatGetsTheNewLocation(newCoordinate: CLLocationCoordinate2D, newDistance: CLLocationDistance) {

    for pin in customPins {

        pin.title = "second title" // ** updates but the callout doesn't reflect it
        pin.subTitle = "second address" // ** updates but the callout doesn't reflect it
        pin.distance = newDistance // ** updates but the callout doesn't reflect it

       // calling these gives me the new data but the annotation blinks and moves really fast to it's new location
       // mapView.removeAnnotation(pin)
       // mapView.addAnnotation(pin)

        UIView.animate(withDuration: 1) {
            pin.coordinate = newCoordinate // this updates and animates to the new location with no problem
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MapView视图用于注释

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

    if annotation.isKind(of: MKUserLocation.self) { return nil }

    guard let annotation = annotation as? CustomPin else { return nil }

    let reuseIdentifier = "CustomPin"

    var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)

    if annotationView == nil {
        annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier)
        annotationView?.canShowCallout = true
        annotationView?.calloutOffset = CGPoint(x: -5, y: 5)

        annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

        annotationView?.image = UIImage(named: "chevronImage")

    } else {
        annotationView?.annotation = annotation
    }

    annotationView?.detailCalloutAccessoryView = nil
    annotationView?.detailCalloutAccessoryView = createCallOutWithDataFrom(customPin: annotation)

    return annotationView
}
Run Code Online (Sandbox Code Playgroud)

为标注创建 UIView

func createCallOutWithDataFrom(customPin: CustomPin) -> UIView {

    let titleText = customPin.title
    let subTitleText = customPin.subTitle
    let distanceText = subTitle.distance // gets converted to a string

    // 1. create a UIView
    // 2. create some labels and add the text from the title, subTitle, and distance and add them as subViews to the UIView
    // 3. return the UIView
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 5

有几个问题:

\n\n
    \n
  1. 您需要@objc dynamic对要观察的任何属性使用限定符。标准标注对和执行键值观察(KVO) 。(注释视图观察到的更改。)titlesubtitlecoordinate

  2. \n
  3. 如果你想观察useriddistance,你也必须做出这些@objc dynamic。请注意,您\xe2\x80\x99ll 必须使其distance成为非可选才能使其可观察:

    \n\n
    var distance: CLLocationDistance\n
    Run Code Online (Sandbox Code Playgroud)\n\n

    所以:

    \n\n
    class CustomAnnotation: NSObject, MKAnnotation {\n    // standard MKAnnotation properties\n\n    @objc dynamic var coordinate: CLLocationCoordinate2D\n    @objc dynamic var title: String?\n    @objc dynamic var subtitle: String?\n\n    // additional custom properties\n\n    @objc dynamic var userId: String\n    @objc dynamic var distance: CLLocationDistance\n\n    init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, userId: String, distance: CLLocationDistance) {\n        self.userId = userId\n        self.distance = distance\n        self.coordinate = coordinate\n        self.title = title\n        self.subtitle = subtitle\n\n        super.init()\n    }\n}\n
    Run Code Online (Sandbox Code Playgroud)
  4. \n
  5. 就像我说的,标准标注观察titlesubtitle。虽然您必须使注释属性可观察,但如果您要构建自己的detailCalloutAccessoryView,则您将必须执行自己的 KVO:

    \n\n
    class CustomAnnotationView: MKMarkerAnnotationView {\n    private let customClusteringIdentifier = "..."\n\n    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {\n        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)\n        canShowCallout = true\n        detailCalloutAccessoryView = createCallOutWithDataFrom(customAnnotation: annotation as? CustomAnnotation)\n        clusteringIdentifier = customClusteringIdentifier\n    }\n\n    required init?(coder aDecoder: NSCoder) {\n        fatalError("init(coder:) has not been implemented")\n    }\n\n    deinit {\n        removeAnyObservers()\n    }\n\n    override var annotation: MKAnnotation? {\n        didSet {\n            removeAnyObservers()\n            clusteringIdentifier = customClusteringIdentifier\n            if let customAnnotation = annotation as? CustomAnnotation {\n                updateAndAddObservers(for: customAnnotation)\n            }\n        }\n    }\n\n    private var subtitleObserver: NSKeyValueObservation?\n    private var userObserver: NSKeyValueObservation?\n    private var distanceObserver: NSKeyValueObservation?\n\n    private let subtitleLabel: UILabel = {\n        let label = UILabel()\n        label.translatesAutoresizingMaskIntoConstraints = false\n        return label\n    }()\n\n    private let userLabel: UILabel = {\n        let label = UILabel()\n        label.translatesAutoresizingMaskIntoConstraints = false\n        return label\n    }()\n\n    private let distanceLabel: UILabel = {\n        let label = UILabel()\n        label.translatesAutoresizingMaskIntoConstraints = false\n        return label\n    }()\n}\n\nprivate extension CustomAnnotationView {\n    func updateAndAddObservers(for customAnnotation: CustomAnnotation) {\n        subtitleLabel.text = customAnnotation.subtitle\n        subtitleObserver = customAnnotation.observe(\\.subtitle) { [weak self] customAnnotation, _ in\n            self?.subtitleLabel.text = customAnnotation.subtitle\n        }\n\n        userLabel.text = customAnnotation.userId\n        userObserver = customAnnotation.observe(\\.userId) { [weak self] customAnnotation, _ in\n            self?.userLabel.text = customAnnotation.userId\n        }\n\n        distanceLabel.text = "\\(customAnnotation.distance) meters"\n        distanceObserver = customAnnotation.observe(\\.distance) { [weak self] customAnnotation, _ in\n            self?.distanceLabel.text = "\\(customAnnotation.distance) meters"\n        }\n    }\n\n    func removeAnyObservers() {\n        subtitleObserver = nil\n        userObserver = nil\n        distanceObserver = nil\n    }\n\n    func createCallOutWithDataFrom(customAnnotation: CustomAnnotation?) -> UIView {\n        let view = UIView()\n        view.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(subtitleLabel)\n        view.addSubview(userLabel)\n        view.addSubview(distanceLabel)\n\n        NSLayoutConstraint.activate([\n            subtitleLabel.topAnchor.constraint(equalTo: view.topAnchor),\n            subtitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            subtitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            subtitleLabel.bottomAnchor.constraint(equalTo: userLabel.topAnchor),\n\n            userLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            userLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            userLabel.bottomAnchor.constraint(equalTo: distanceLabel.topAnchor),\n\n            distanceLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            distanceLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            distanceLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor)\n        ])\n\n        if let customAnnotation = customAnnotation {\n            updateAndAddObservers(for: customAnnotation)\n        }\n\n        return view\n    }\n}\n
    Run Code Online (Sandbox Code Playgroud)
  6. \n
\n\n

得出:\n动画标注更改

\n

  • 谢谢!你为我节省了大量的研究工作。我开始看着不同的豆荚,然后我就“呃……”。这是一个非常有力的答案,它涵盖了很多基础知识,几乎涵盖了自定义注释和标注所需的一切。我希望更多的人会遇到,因为它应该有大量的赞成票。无论如何,谢谢,我真的很感激:) (2认同)