Swift-如何在自定义MKAnnotation标注中更新数据? [英] Swift -How to Update Data in Custom MKAnnotation Callout?

查看:90
本文介绍了Swift-如何在自定义MKAnnotation标注中更新数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的mapView有一个自定义注释.我最初使用一些数据在其上设置坐标,标题(例如第一个标题"),subTitle(例如第一个地址"),userId和距离(例如0米)属性.我将其添加到mapView和数组中以备后用.一切正常,将其显示在mapView上,我按一下,标注就会显示该初始数据.

I have a custom annotation for my mapView. I initially set the coordinate, title (eg. "first title"), subTitle (eg. "first address"), userId, and a distance (eg. 0 meters) property on it with some data. I add it to the mapView and to an array for later use. Everything works, it shows on the mapView, I press it and the callout shows that initial data.

我后来得到更新,该标注的位置已更改.我遍历数组,并使用坐标,标题(例如新标题"),subTitle(例如新地址")和距离(例如100米)属性的新数据更新标注.我还将标注从原始位置动画化为新位置.动画效果很好,标注从A点移到B点.

I later get updated that the location for that callout has changed. I loop through the array and update the callout with new data for the coordinate, title (eg. "new title"), subTitle (eg. "new address"), and distance (eg. 100 meters) properties. I also animate the callout from it's original location to it's new location. The animation works fine and the callout moves from point A to point B.

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

The problem is when I tap the annotation the old data gets shown on the callout instead of the new data.

我使用calloutAccessoryControlTapped来推送新的vc.当我在其中放置一个断点时,自定义引脚将包含所有新数据.该标注似乎发生了错误.

I use calloutAccessoryControlTapped to push on a new vc. When i put a breakpoint there the custom pin has all the new data. The error seems to happen with the callout.

我该如何解决?

我不想从mapView中清除所有注释,所以这不是一个选择.我调用了mapView.removeAnnotation(customPin)mapView.addAnnotation(customPin),它们解决了该引脚的问题,但是当移除该引脚并将其添加回地图时,它会闪烁,然后当它动画到它的新位置时,它看起来就不稳定了.

I don't want to clear all the annotations from the mapView so that's not an option. I call mapView.removeAnnotation(customPin) and mapView.addAnnotation(customPin) which fixes the problem for that pin but there is a blink when the pin is removed and added back to the map and then when it animates to it's new location it looks choppy.

自定义注释

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()
    }
}

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

First time the annotation gets set with initial data

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)
    }
}

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

Second time the annotation gets set with New Data

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
        }
    }
}

MapView viewFor注释

MapView viewFor annotation

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
}

创建用于标注的UIView

Creation of UIView for Callout

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
}

推荐答案

有几个问题:

  1. 您需要对要观察的任何属性使用@objc dynamic限定符.标准标注在键值观察(KVO) >和subtitle. (并且注释视图观察到对coordinate的更改.)

  1. You need to use the @objc dynamic qualifier for any properties you want to observe. The standard callout performs Key-Value Observation (KVO) on title and subtitle. (And the annotation view observes changes to coordinate.)

如果要观察useriddistance,则还必须制作这些@objc dynamic.请注意,您必须使distance为非可选以使其可观察:

If you want to observe userid and distance, you have to make those @objc dynamic as well. Note, you’ll have to make distance be non-optional to make that observable:

var distance: CLLocationDistance

所以:

class CustomAnnotation: NSObject, MKAnnotation {
    // standard MKAnnotation properties

    @objc dynamic var coordinate: CLLocationCoordinate2D
    @objc dynamic var title: String?
    @objc dynamic var subtitle: String?

    // additional custom properties

    @objc dynamic var userId: String
    @objc dynamic var distance: CLLocationDistance

    init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String, userId: String, distance: CLLocationDistance) {
        self.userId = userId
        self.distance = distance
        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle

        super.init()
    }
}

  • 就像我说的那样,标准标注遵循titlesubtitle.虽然必须使注释属性可观察,但是如果要构建自己的detailCalloutAccessoryView,则必须做自己的KVO:

  • Like I said, the standard callout observes title and subtitle. While you have to make the annotation properties observable, if you’re going to build your own detailCalloutAccessoryView, you’re going to have to do your own KVO:

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

  • 产生:

    这篇关于Swift-如何在自定义MKAnnotation标注中更新数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆