swiftannotationsmkmapview

Swift Annotation Not Placed on MapView because viewForAnnotation not called


I am working on an app that places a pin at the users current location as well as places pins based on input from the user for locations. The current location annotation is being placed fine, however when trying to place the user's input coordinates, the annotation is not placed.

I have added the annotation properly using map.addAnnotation(annotation) for which annotation.coordinate (for New York) is CLLocationCoordinate2D(latitude: 40.713054, longitude: -74.007227999999998).

I found that the viewForAnnotation method is not being called and I think this is the reason why the annotation is not being placed (printing the annotation results in nothing). I have a test app that requests input coordinates and it works in placing the annotation. Also in the test app, printing the annotation in viewForPrints out a value.

Below I have pasted some of the code I think is relevant to the issue. Please comment if you need more.

import UIKit
import MapKit
import CoreLocation
class MapVC: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {



@IBOutlet weak var map: MKMapView!

override func viewDidLoad() {
    super.viewDidLoad()
    map.delegate = self
}

This function below takes the values from another class and converts the coordinates into CLLocationCoordinate2D type. As mentioned above, annotation.coordinate (for New York) yields CLLocationCoordinate2D(latitude: 40.713054, longitude: -74.007227999999998), so add works in saving the coordinates.

func add(newLocation location_one:[String:Any]) {
    let momentaryLat = (location_one["latitude"] as! NSString).doubleValue
    let momentaryLong = (location_one["longitude"] as! NSString).doubleValue
    
    let annotation = MKPointAnnotation()
    annotation.title = location_one["title"] as? String
    annotation.coordinate = CLLocationCoordinate2D(latitude: momentaryLat as CLLocationDegrees, longitude: momentaryLong as CLLocationDegrees)
    


    map.addAnnotation(annotation) //not sure if this adds the annotation!

    self.map.centerCoordinate = annotation.coordinate
}

func mapView(_ map: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

    if (annotation is MKUserLocation) {
        return nil
    }
   
    let identifier = "pinAnnotation"
    var annotationView = map.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
    
    if annotationView == nil {
        annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView?.canShowCallout = true
    
    } else {
        annotationView?.annotation = annotation
    
    }
    map.showAnnotations(map.annotations, animated: true)
    return annotationView
}

Solution

  • First,

    func mapView(_ map: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?

    This is a delegate method and is not used for setting the annotations, It simply

    1. Returns the view associated with the specified annotation object, i.e. The annotation view to display for the specified annotation or nil if you want to display a standard annotation view.`

    2. If you do not implement this method, or if you return nil from your implementation for annotations other than the user location annotation, the map view uses a standard pin annotation view.`

    Check it out here-

    https://developer.apple.com/reference/mapkit/mkmapviewdelegate/1452045-mapview

    The addAnnotation() method is enough for adding the default pin annotations. It may not be getting called because you may have called the

    func add(newLocation location_one:[String:Any]) from background queue, which calls addAnnotatinon() method inside.

    If you are calling add method in some other controller that too on a background queue, may be some completion handlers, then you need to do it explicitly and call the add annotation method on the main thread

    DispatchQueue.main.async {
        self.map.addAnnotation(annotation) //Yes!! This method adds the annotations
    }
    

    For debugging purposes change your controller to this, even removing the delegates, then also you will get to see the annotations.

    import UIKit
    import MapKit
    
    class ViewController:  UIViewController{
    
    @IBOutlet weak var map: MKMapView!
    
          override func viewDidLoad() {
              super.viewDidLoad()
              let annotation = MKPointAnnotation()
              annotation.title = "Test Location"
              annotation.coordinate = CLLocationCoordinate2D(latitude: 40.71304, longitude: -74.0072)
              self.map.addAnnotation(annotation)
          }
    }
    

    So do ensure-

    1. The addAnnotation method is called from the main thread
    2. viewForAnnoation method - you use it to provide customisation to the default pin of the annotation.