如何更新MKPinAnnotationView的信息?

时间:2016-12-24 07:33:47

标签: ios mkmapview mkpinannotationview mkmapviewdelegate mkpointannotation

我使用MKMapViewMKPointAnnotation过去有过一些经验,我曾经在地图上放了一些图钉。 这次我试图更进一步,使用MKPinAnnotationView,写一个标签和一些引脚。

不幸的是,它并没有像我期望的那样完成。

这是我想要做的:

我有一张地图(一个MKMapView对象),当我触摸它时,我在触摸点放了一个引脚,然后进行了一些计算,这给了我地图上的第二个点。我在地图上放置了第二个引脚(位于第二个点),在最后一个引脚上我想放一个标签,说" Hello Second!",但这个标签需要在引脚时更新改变地方。

以下是相关代码:

class ViewController: UIViewController, MKMapViewDelegate {
    var mapView:MKMapView!, touchPoint,secondPoint:MKPointAnnotation!

    override func viewDidLoad() {
        super.viewDidLoad()
        mapView = MKMapView()
        ...........
        let mapTap = UITapGestureRecognizer(target: self,
                                            action: #selector(ViewController.mapTouchHandler))
        mapView.addGestureRecognizer(mapTap)
    }


    func mapTouchHandler(gesture:UITapGestureRecognizer) {
        ...........
        // Compute map coordinates for the touch point (tapGeoPoint).

        if touchPoint == nil {
            touchPoint = MKPointAnnotation()
            mapView.addAnnotation(touchPoint);
        }

        touchPoint.coordinate = CLLocationCoordinate2D(latitude: tapGeoPoint.latitude,
                                                       longitude: tapGeoPoint.longitude)
        ...........
        computeSecondPoint(url: someComputedURL)
    }


    func computeSecondPoint(url searchURL:String) {
        let reqURL = NSURL(string: searchURL)!, session = URLSession.shared,
        task = session.dataTask(with: reqURL as URL) {
            (data: Data?, response: URLResponse?, error: Error?) in
            if error == nil {
                do {let allData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSArray
                    .................
                    // Compute map coordinates for the second point (secondPointCoord).

                    if self.secondPoint == nil {
                        self.secondPoint = MKPointAnnotation()
                        self.mapView.addAnnotation(self.secondPoint)
                    }

                    DispatchQueue.main.async {
                        () -> Void in
                        self.secondPoint.coordinate = CLLocationCoordinate2D(latitude: secondPointCoord.latitude,
                                                                             longitude: secondPointCoord.longitude)
                        self.secondPoint.title = "Hello Second -TITLE!"
                        //* I want to update the label for this pin (attached to the secondPoint) too.
                    }
                } catch let error as NSError {print(error.localizedDescription)}
            } else {
                print("Error inside \(#function):\n\(error)")
            }
        }

        task.resume()

    }


    func mapView(_ mapView: MKMapView,
                 viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = "pin"
        var view: MyPinAnnotationView
        if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
            as? MyPinAnnotationView {
            dequeuedView.annotation = annotation
            view = dequeuedView
        } else {
            view = MyPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)

            if ((annotation.coordinate.latitude != touchPoint.coordinate.latitude) ||
                (annotation.coordinate.longitude != touchPoint.coordinate.longitude)) {//* I need a better test to check that this not touchPoint!
                view.pinTintColor = UIColor.blue
                view.canShowCallout = true
                view.calloutOffset = CGPoint(x: -7, y: 0)
                view.setInfo(title: "Hi Start Label!")
            } else {
                view.pinTintColor = UIColor.red
                view.canShowCallout = false
            }
        }
        return view
    }
}

这是MyPinAnnotationView类:

import UIKit
import MapKit

class MyPinAnnotationView: MKPinAnnotationView {
    let information:UILabel = UILabel(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 70.0, height: 30.0)))

    func setInfo(title : String)
    {
        information.text = title
        information.textAlignment = .center
        self.addSubview(information)
    }


    func hideInfo() {
        information.removeFromSuperview()
    }
}

注释标记为// *的行显示我需要帮助的地方。

  • 第一个问题,我想更新secondPoint上的标签,但我不知道要使用哪些代码。行:

    // *我想更新此引脚的标签(附加到secondPoint)。

现在标签显示为第一组,但我不知道如何更新它。

  • 第二个问题,必须有一个更好的方法来测试我正在处理哪个引脚。行:

    // *我需要一个更好的测试来检查这不是touchPoint!

1 个答案:

答案 0 :(得分:1)

如果希望注释视图显示一些随注释更改而更新的文本,请在注释上使用KVO。因此,首先,创建一个模型对象,即注释,其中包含要观察的新属性:

class MyAnnotation: NSObject, MKAnnotation {
    dynamic var title: String?
    dynamic var subtitle: String?
    dynamic var coordinate: CLLocationCoordinate2D
    dynamic var information: String?

    init(title: String? = nil, subtitle: String? = nil, coordinate: CLLocationCoordinate2D, information: String? = nil) {
        self.title = title
        self.subtitle = subtitle
        self.coordinate = coordinate
        self.information = information
    }
}

我已经调用了这个新属性information,但你可能想出一个更好的名称来捕获这个属性的功能意图。但希望它说明了这个想法。这里的关键点是,如果此属性可能在以后发生变化,我们将要dynamic,以便我们可以使用KVO来观察这些变化。

class MyPinAnnotationView: MKPinAnnotationView {
    private let informationLabel = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 70.0, height: 30.0)))

    private var observerContext = 0

    override var annotation: MKAnnotation? {
        willSet {
            removeObserverIfAny()
        }
        didSet {
            if let annotation = annotation as? MyAnnotation {
                annotation.addObserver(self, forKeyPath: #keyPath(MyAnnotation.information), context: &observerContext)
                informationLabel.text = annotation.information
            }
        }
    }

    deinit {
        removeObserverIfAny()
    }

    private func removeObserverIfAny() {
        if let oldAnnotation = annotation as? MyAnnotation {
            oldAnnotation.removeObserver(self, forKeyPath: #keyPath(MyAnnotation.information))
        }
    }

    func showInformation() {
        addSubview(informationLabel)
    }

    func hideInformation() {
        informationLabel.removeFromSuperview()
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard context == &observerContext else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            return
        }

        if let annotation = annotation as? MyAnnotation, let information = annotation.information {
            informationLabel.text = information
        }
    }

}

extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        if annotation is MKUserLocation { return nil }

        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MyPinAnnotationView
        if annotationView == nil {
            annotationView = MyPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            annotationView?.canShowCallout = true
        } else {
            annotationView?.annotation = annotation
        }
        annotationView?.showInformation()
        return annotationView
    }
}

我已将标签的名称更改为informationLabel,以使其更加明确,因为它是一个视图,而不是与我们的新{的模型属性information混淆{1}}课程。另外,我建议使用比MyAnnotationMyAnnotation更有意义的类名,也许使用一些更好地捕获这两个类的功能意图的名称。

无论如何,正如您所看到的,当您为此注释视图设置MyPinAnnotationView时,它会更新标签annotation。但它也通过KVO观察注释的新text属性,因此如果此属性稍后更改,则视图将相应更新。