如何更新MKPinAnnotationView的信息?

我有过使用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!

如果希望注释视图显示一些随注释更改而更新的文本,请在注释上使用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 ,但你可能想出一个更好的名称来捕获这个属性的function意图。 但希望它说明了这个想法。 这里的关键点是,如果此属性可能在以后发生变化,我们将要使其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 ,使其更加明确,它是一个视图,而不是与我们的新MyAnnotation类的模型属性, information混淆。 另外,我建议比MyAnnotationMyPinAnnotationView更有意义的类名,也许使用一些更好地捕获这两个类的function意图的名称。

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