3

我过去有一些使用MKMapViewand的经验MKPointAnnotation,我曾经在地图上放置一些图钉。这次我想更进一步,用MKPinAnnotationView, 写一个标签和一些别针。

不幸的是,它并没有像我预期的那样工作。

这是我想做的事情:

我有一张地图(一个 MKMapView 对象),当我触摸它时,我在触摸点上放了一个大头针,然后执行一些计算,这给了我地图上的第二个点。我在地图上放了第二个图钉(位于第二个点),在最后一个图钉上我想放一个标签,说“你好,第二个!”,但是当图钉改变位置时,这个标签需要更新。

以下是相关代码:

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

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

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

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

4

1 回答 1

3

如果您希望注释视图显示一些随着注释更改而更新的文本,请在注释上使用 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的模型属性混淆。另外,我建议使用比and更有意义的类名,也许使用一些能更好地捕捉这两个类的功能意图的名称。informationMyAnnotationMyAnnotationMyPinAnnotationView

无论如何,如您所见,当您annotation为此注释视图设置 时,它会更新 label text。但是它也information通过 KVO 观察注解的新属性,所以如果以后这个属性发生变化,视图也会相应地更新。

于 2016-12-24T10:09:23.830 回答