点击集合视图单元以向地图添加注释

Posted

技术标签:

【中文标题】点击集合视图单元以向地图添加注释【英文标题】:Tapping a collection view cell to add annotations to a map 【发布时间】:2020-06-01 18:56:05 【问题描述】:

现在我有一个地图和一个显示地图图标的集合视图。我想制作它,以便选择其中一个图标时,它会在地图上显示该特定类别中的这些不同的位置;类似于苹果地图。

 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



目前,没有调用放置相关类别注释的函数。当我的收藏视图中的一个项目被点击以达到预期的结果时,有没有办法调用这些,或者有没有更好的方法来完成这个?

【问题讨论】:

【参考方案1】:

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

    当用户点击 CollectionView 的单元格时收到通知,并通知 MapViewController 要显示的注释集 为您的 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(_)之前添加这一行。

第二步

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

MapViewController 是实现 MKMapViewDelegate 的理想选择:

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

    mapView.delegate = self
    ...
  

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

您可以采取的主要方法有两种:

简单地返回一个MKPinAnnotationView:一个裸露的、标准的ios风格的pin? 除了颜色之外,您无法自定义它的视觉外观
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? 
  let pinView = MKPinAnnotationView()
  pinView.pinTintColor = .green
  return pinView

返回MKAnnotaionView 或子类化您自己的,以完全自定义注释的外观。在以下示例中,我只是使用 UIImage。
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? 
  let iconView = MKAnnotationView()
  iconView.image = UIImage(named: "pin_icon")
  return iconView

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

注意:如果您在地图中插入了大量注释,这两种方法可能会导致性能问题。在这种情况下,您需要register 和dequeue 可重用注释视图,以便地图可以回收它们。我建议你看一些quick tutorial 并查阅MapKit 文档以加深主题。

【讨论】:

非常感谢您的回答。唯一的问题是我的 ContentViewController 没有对 MapViewController 的引用。如何添加参考? 这首先取决于这两个 VC 是如何创建的。我怀疑它们可能是同一个“页面”的一部分(它们是否并排出现在同一个屏幕上?)。如果是这种情况,它们是容器 ViewController 的子级吗?它们是完全由 Storyboard 还是由代码创建的? 是的,出现在同一个屏幕上。它们是由故事板创建的。我在故事板的上方添加了一张图片。 这不是一个理想的设置:您将两个视图控制器 A、B 作为父视图控制器 C 的子级嵌入,但在您的情况下,A (ContentVC) 依赖于 B (MapVC)。您可以在子 VC 使用父 VC 中的 prepareForSegue:sender: 方法嵌入时将数据传递给子 VC,但您无法控制先嵌入哪个子 VC。要解决这个问题,您可以: - 使用 NotificationCenter 将两个 VC 解耦 - 将两个 VC 合并为一个 - 通过手动实例化 A 和 B 来保持相同的结构,而不使用 Storyboard 我建议你使用几个断点来观察代码流并在它停止的地方进行调试。您必须验证是否调用了以下函数:collectionView(collectionView:didSelectItemAt:) -> setXXXXAnnotations() -> mapView(mapView:,viewFor:)

以上是关于点击集合视图单元以向地图添加注释的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将按钮添加到集合视图单元格

Xcode将集合视图单元添加到界面生成器上的集合视图

要在开头添加的集合视图单元格

集合视图单元格内的表视图?

在表格视图单元格中添加集合视图 swift 3

如何在可扩展的表格视图单元中制作动态集合视图