点击 collection 视图单元格向地图添加注释

Tapping a collection view cell to add annotations to a map

现在我有一张地图和一个显示地图图标的 collection 视图。我想让它在选择其中一个图标时显示地图上该特定类别内的那些不同位置;类似于苹果地图。

 import UIKit
 import MapKit

 //Creating an outline for the different locations of places on the map
 struct PlacesOnMap {
 var name: String
 var latitude: Double
 var longitude: Double

init(name: String, latitude: Double, longitude: Double) {
self.name = name
self.latitude = latitude
self.longitude = longitude
}
}

 class ContentViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

 //Properties of collectionView, that determine number of items in the section and allows a single cell to be reused over and over again
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imageArray.count
 }

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
cell.mapIconImage.image = imageArray[indexPath.row]
cell.mapIconLabel.text! = imageNameArray[indexPath.row]
return cell
}

 //creating a constant for variables within the collection view such as the image and the title of the image
  let imageArray = [UIImage(named: "1"), UIImage(named: "2"), UIImage(named: "3")]
 let imageNameArray = ["image 1", "image 2", "image 3"]

 @IBOutlet var mapView: MKMapView!

 var places = [PlacesOnMap(name: "place 1", latitude: 28.551700, longitude: -81.374800),
 PlacesOnMap(name: "place 2", latitude: 28.553018, longitude: -81.374206),
 PlacesOnMap(name: "place 3", latitude: 28.553019, longitude: -81.367839)
]
 var buildings = [PlacesOnMap(name: "place 1", latitude: 28.556969, longitude: -81.364319),
 PlacesOnMap(name: "place 2", latitude: 28.558126, longitude: -81.364725)
]
 var recreation = [PlacesOnMap(name: "place 1", latitude: 28.54693, longitude: -81.393071),
 PlacesOnMap(name: "place 2", latitude: 28.538523, longitude: -81.385399),
 PlacesOnMap(name: "place 3", latitude: 28.542817, longitude: -81.378117),
 PlacesOnMap(name: "place 4", latitude: 28.538985, longitude: -81.404694)
]

override func viewDidLoad() {
super.viewDidLoad()

mapView?.delegate = self
}

func setPlacesAnnotations() {
let places = places.map { placeOnMap -> MKPointAnnotation in
let place = MKPointAnnotation()
place.coordinate =  CLLocationCoordinate2D(latitude: placeOnMap.latitude, longitude: placeOnMap.longitude)
place.title = placeOnMap.name
return place
}
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(places)
}

func setBuildingsAnnotations() {
let places = buildings.map { placeOnMap -> MKPointAnnotation in
let place = MKPointAnnotation()
place.coordinate =  CLLocationCoordinate2D(latitude: placeOnMap.latitude, longitude: placeOnMap.longitude)
place.title = placeOnMap.name
return place
}
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(places)
}

func setRecreationAnnotations() {
let places = recreation.map { placeOnMap -> MKPointAnnotation in
let place = MKPointAnnotation()
place.coordinate =  CLLocationCoordinate2D(latitude: placeOnMap.latitude, longitude: placeOnMap.longitude)
 place.title = placeOnMap.name
 return place
 }
 mapView.removeAnnotations(mapView.annotations)
 mapView.addAnnotations(places)
 }

 //calls the functions up above when a cell from the collection view is selected
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
switch (indexPath.item) {
case 0: setBuildingsAnnotations()
case 1: setPlacesAnnotations()
case 2: setRecreationAnnotations()
default:
    break
}
}
}

当前,放置受尊重类别的注释的函数未被调用。当点击我的 collection 视图中的项目以实现预期结果时,是否有一种方法可以调用这些方法,或者是否有更好的方法来完成此操作?

为了使用您提供的代码实现您的要求,缺少 2 个重要步骤:

  1. 当用户点击 CollectionView 的单元格时收到通知,并通知 MapViewController 要显示的注释集
  2. 为您的 MKMapView 提供一个 MKMapViewDelegate

第 1 步

为简洁起见,我假设您的 ContentViewController 持有对 MapViewController 的引用,以便它可以在点击单元格时调用适当的 setAnnotations() 函数.

class ContentViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
  var mapViewController: MapViewController!
  let imageArray = ...
}

接下来,我们需要在用户点击其中一个单元格时得到通知,为此我们需要使用 UICollectionViewDelegate 协议的特定回调:

class ContentViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
  ...
  func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    switch (indexPath.item) {
    case 0:
      mapViewController.setPlacesAnnotations()
    case 1:
      mapViewController.setBuildingsAnnotations()
    case 2:
      mapViewController.setRecreationAnnotations()
    }
  }
}

这样,当用户选择您的单元格之一时,MapViewController 中的三个 setXYZAnnotations 函数之一被调用

在进入下一步之前,我们有一个小问题需要解决:MKMapView 保留所有添加到它的注释,直到它们被删除。这意味着每次您调用其中一个 setXYZAnnontation 函数(因此,每次用户点击一个单元格)时,都会在地图上附加一组新的注释。 我们有一个简单的方法来丢弃之前插入地图的所有注释:

mapView.removeAnnotations(mapView.annotations)

这样,我们告诉地图视图清除它当前持有的所有注释。确保在每个 setXYZAnnontation 函数中 调用 mapView.addAnnotations(_) 之前添加此行。

第 2 步

当您在地图上添加注释或叠加层时,需要一种方式来告诉它如何您希望如何呈现这些元素。这是由 MKMapViewDelegate 完成的,就像 UICollectionView 使用 UICollectionViewDataSource 来知道 UICollectionViewCell 用于单元格。

MapViewController 是实现 MKMapViewDelegate 的理想选择:

class MapViewController: UIViewController, MKMapViewDelegate {
  override func viewDidLoad() {
    super.viewDidLoad()

    mapView.delegate = self
    ...
  }
}

现在,你需要在MapViewController中实现的协议方法是mapView(_:viewFor:),这个函数要你return一个MKAnnotationView关联到你之前插入的指定注解对象。

您主要可以采用两种方法:

  • 只是 return 一个 MKPinAnnotationView:一个裸露的标准 iOS 风格的图钉
    除了颜色之外,您不能自定义它的视觉外观
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
  let pinView = MKPinAnnotationView()
  pinView.pinTintColor = .green
  return pinView
}
  • Return MKAnnotaionView 或子类化您自己的注释,以完全自定义注释的外观。在下面的示例中,我只是使用 UIImage.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
  let iconView = MKAnnotationView()
  iconView.image = UIImage(named: "pin_icon")
  return iconView
}

当然,在您的应用中,您希望根据用户选择的特定注释集来区分注释视图的外观。

注意:如果您在地图中插入了大量注释,这两种方法可能会导致性能问题。在那种情况下,您需要 register and dequeue reusable annotation views, so that the map can recycle them. I suggest you to have a look at some quick tutorial and consult the MapKit 文档来加深主题。