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:
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.
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:
MKPinAnnotationView
: a bare, standard iOS-styled pinfunc mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let pinView = MKPinAnnotationView()
pinView.pinTintColor = .green
return pinView
}
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.