Search code examples
swiftswiftuimapkitmapkitannotation

Adding annotations to MapKit in UIRepresentable


I'm working in SwiftUI and using MKMapView to show a map, annotations, and overlays on a view.

I was using the new Map() but the lack of overlay support has pulled me back to UIKit Representable.

When I was using Map it was easy to add annotations, but when using UIKitRepresentable I'm a bit confused on where to put the data, and how to make annotations from an array pulled from a network call.

Everything I've read has been either in Obj-C or adding a single annotation point. I'm trying to add (at present ~800) which is why I wanted to take advantage of the MKMapView in its reusability and clustering.

This is what I have at the moment:

struct UIMapView: UIViewRepresentable {
  
  @EnvironmentObject var dataModel: DataModel
  @EnvironmentObject var mapViewModel: MapViewModel

  func makeCoordinator() -> Coordinator { Coordinator() }
  
  func makeUIView(context: Context) -> MKMapView {
    let view = mapViewModel.mapView
    drawOverlayRing(view: view)
    view.delegate = context.coordinator
    return view
  }

  func updateUIView(_ uiView: MKMapView, context: Context) {}

  class Coordinator: NSObject, MKMapViewDelegate {
    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
      }
      return annotationView
    }
  }

  func getAnnotations(view: MKMapView) {
    for location in dataModel.locations {
      let annotation = MKPointAnnotation()
      annotation.title = location.title
      annotation.coordinate = CLLocationCoordinate2D(
        latitude: location.latitude,
        longitude: location.longitude
      )
      view.addAnnotation(annotation)
    }
  }
}

Solution

  • I sometimes put my annotations in updateUIView, something like this:

    func updateUIView() {
        // remove the old ones   
        uiView.removeAnnotations(uiView.annotations)
        uiView.addAnnotations(toMapAnnotations(locations: dataModel.locations))
    }
    
    func toMapAnnotations(locations: [CLLocationCoordinate2D]) -> [MapAnnotation] {
        return locations.map { MapAnnotation(location: $0) }
    }
    
    final class MapAnnotation: NSObject, MKAnnotation {...}