swiftxcodemkmapviewmkannotationmkannotationview

How can I create multiple custom annotations in a map view?


The idea is simple. I have an array of StopData() which a custom struct I created to display different locations a mapView by doing a for each loop. My code looks a little like this:

var route = [StopData]()

struct StopData {
    var addr: String?
    var coord: CLLocationCoordinate2D?
    var number: Int?
    var completed = false
}

func addStopsToMap() {
    mapView.removeAnnotations(mapView.annotations)
    for stop in route {
        let annotation = MKPointAnnotation()
        annotation.coordinate = stop.coord!
        annotation.title = "\(stop.number!)"
        annotation.subtitle = stop.addr
        mapView.addAnnotation(annotation)
    }
}

func attemptLocationAccess() {
    // For use in foreground
    self.locationManager.requestWhenInUseAuthorization()
    
    if CLLocationManager.locationServicesEnabled() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.startUpdatingLocation()
    }
}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    return NonClusteringAnnotation(annotation: annotation, reuseIdentifier: "NonClusteringAnnotation")
}

// MARK: - Location manager functions
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    return
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    locationManager.requestWhenInUseAuthorization()
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    guard status == .authorizedWhenInUse else {
        return
    }
    manager.requestLocation()
}

I also have a custom class like the following:

class NonClusteringAnnotation: MKMarkerAnnotationView {
    override var annotation: MKAnnotation? {
        willSet {
            displayPriority = .required
        }
    }
}

I have three main things I would like to accomplish.

With this code, every pin is shown on the map and does not cluster. However, that also includes the current location blue dot which appears as a red pin like the rest of all the annotations. So I was wondering how can I make multiple custom annotation views and assign them only certain annotations whilst still retaining the blue dot indicating the users current location?

What I tried so far is adding a Boolean variable like willUseCustomAnnotation which would trigger a different return value like this:

func addStopsToMap() {
    mapView.removeAnnotations(mapView.annotations)
    willUseCustomAnnotation = true
    for stop in route {
        let annotation = MKPointAnnotation()
        annotation.coordinate = stop.coord!
        annotation.title = "\(stop.number!)"
        annotation.subtitle = stop.addr
        mapView.addAnnotation(annotation)
    }
    willUseCustomAnnotation = false
}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    if willUseCustomAnnotation {
        return NonClusteringAnnotation(annotation: annotation, reuseIdentifier: "NonClusteringAnnotation")
    } else {
        return nil
    }
}

This does not work though because I presume it first adds all the annotations to mapview and then creates the view for them all at once.

I read on forums to change the view for the annotation you must create your own view in the viewForAnnotation function however that is only if you want a single custom view and not multiple custom view like I want. I'm new to MapKit so help will be really appreciated.


Solution

  • It's been a long time since I've used an MKMapView and custom annotations (it was in Objective-C.)

    I seem to remember that in your mapView(_:viewFor:) function you need to test to see if the annotation being passed to you is an MKUserLocation. If it is, return nil:

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
       guard !annotation is MKUserLocation else { 
           //This is the user location. Return nil so the system uses the blue dot.
           return nil  
       }
       //Your code to create an return custom annotations)
    }