1

我已經有一些過去使用MKMapViewMKPointAnnotation的經驗,我曾經在地圖上放置了一些別針。 這次我想更進一步,使用MKPinAnnotationView來寫一個標籤和一些引腳。如何更新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!

+0

如果你不介意,你會分享你的演示嗎?我可以理解你的問題,併發布它的答案 –

+0

好,但讓我知道你需要知道,這是不是在我的文章。我已經在這篇文章中分享了所有我能想到的主題。 – Michel

+0

檢查此答案我認爲這將幫助你http://stackoverflow.com/questions/24467408/swift-add-mkannotationview-to-mkmapview –

回答

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,我們的新MyAnnotation類的混淆。此外,我建議比MyAnnotationMyPinAnnotationView更有意義的類名稱,或許使用更好的名稱來捕獲這兩個類的功能意圖。

無論如何,如您所見,當您爲此批註視圖設置annotation時,它會更新標籤text。但它也通過KVO觀察註釋的新information屬性,因此如果此屬性稍後更改,則視圖將相應更新。

+0

不,我不想看到標題,因爲你提到的原因。但我會記住你的觀察者的想法,看看我能否用它做點什麼。我想更新信息UILabel(請參閱我的文章)。 – Michel

+0

我當然需要工作,從你的想法開始。我不得不說,只是通過閱讀你的代碼,在這一點上我不太瞭解。最終我想更新信息的內容(UILabel),當用戶觸摸顯示屏並且secondPoint改變位置時。一旦我看到它的工作原理,我希望你的建議能使我做到這一點。 – Michel

+0

@Michel - 是的,這就是爲什麼我們使用觀察者模式,以便您可以稍後更新一些註釋屬性,並且註釋視圖將相應地更新。只要觀察一下除「title」之外的其他屬性。看到我上面的擴展答案。 – Rob