简体   繁体   中英

Tapping a collection view cell to add annotations to a map

Right now I have a map and a collection view that displays icons for the map. I want to make it so that when one of the icons is selected it displays those different locations within that specific category on the map; similar to Apple Maps.

 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
}
}
}

Currently, the functions that places the annotations of the respected category are not being called. Is there a way to call these when an item from my collection view is tapped in order to achieve the desired results, or is there a better way to go about accomplishing this?

在此处输入图像描述

There are 2 important missing steps in order to achieve what you asked with the code you provided:

  1. Be notified when the user taps a cell of your CollectionView and inform MapViewController of the set of annotation to display
  2. Provide a MKMapViewDelegate to your MKMapView

Step 1

For the sake of brevity, I'm going to assume that your ContentViewController holds a reference to MapViewController , so that it can call the appropriate setAnnotations() function when a cell is tapped.

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

Next, we need to be informed when the user taps on one of those cells, for that we need to use a specific callback of UICollectionViewDelegate protocol:

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()
    }
  }
}

In this way, one of the three setXYZAnnotations function in MapViewController is called each time the user selects one of your cell.

We have a small issue to tackle before moving to the next step: MKMapView retains all the annotations added to it until those are removed. This means that each time you call one of the setXYZAnnontation function (hence, each time the user taps on a cell) a new set of annotation is appended to the map. We have a simple way to discard all the annotations that were previously inserted into the map:

mapView.removeAnnotations(mapView.annotations)

This way, we're telling the map view to purge all the annotation it is currently holding. Make sure to add this line right before calling mapView.addAnnotations(_) in each one of your setXYZAnnontation functions.

Step 2

When you add annotations or overlays on a map, it needs a way to be told how exactly you want those element to be rendered. This is accomplished by a MKMapViewDelegate , just like a UICollectionView uses a UICollectionViewDataSource to know what UICollectionViewCell to use for a cell.

MapViewController is a good candidate to implement MKMapViewDelegate:

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

    mapView.delegate = self
    ...
  }
}

Now, the protocol method you need to implement in MapViewController is mapView(_:viewFor:) , this function wants you to return an MKAnnotationView to associate with the specified annotation object you previously inserted.

There are mainly two approaches you can take:

  • Simply return a MKPinAnnotationView : a bare, standard iOS-styled pin
    You can't customise the visual appearance of it other than its color
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
  let pinView = MKPinAnnotationView()
  pinView.pinTintColor = .green
  return pinView
}
  • Return a MKAnnotaionView or subclass your own one, to fully customise the look of your annotation. In the following example I'm simply using a UIImage.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
  let iconView = MKAnnotationView()
  iconView.image = UIImage(named: "pin_icon")
  return iconView
}

Of course, in your app you'd want to differentiate the look of the annotation view depending on the specific set of annotations selected by the user.

Note : if you have a large number of annotations inserted in a map, these two approaches might lead to performance problems. In that case you need to 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 documentation to deepen the subject.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM