swiftuimapkitmkannotationuiviewrepresentable

How can I show/hide the title of my MKPointAnnotation() pin after I set it inside of my UIViewRepresentable updateUIView (SwiftUI)?


I would like to toggle the visibility of the title of my MKPointAnnotation after I tap the pin. I tried changing the title directly in
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) but it tells me that it's only a get property and I cannot change it inside of my Coordinator class.

Any help would be much appreciated!

Here is the relevant code...

import SwiftUI
import MapKit
import CoreLocationUI

struct MapViewTest: UIViewRepresentable {
    
    @EnvironmentObject var viewModel: MapViewModel
    
    @Binding var region: MKCoordinateRegion
    @Binding var lineCoordinates: [[CLLocationCoordinate2D]]
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        mapView.region = region
        mapView.showsUserLocation = true
        
        return mapView
    }
    
    func updateUIView(_ view: MKMapView, context: Context) {
        view.setRegion(region, animated: true)
        
        for i in viewModel.locations {
            let pin = MKPointAnnotation()
            pin.coordinate = i.coordinate
            pin.title = i.name
            view.addAnnotation(pin)
        }
        
        for i in lineCoordinates{
            let polyline = MKPolyline(coordinates: i, count: i.count)
            view.addOverlay(polyline)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    
}

class Coordinator: NSObject, MKMapViewDelegate {
        
    var parent: MapViewTest
    
    init(_ parent: MapViewTest) {
        self.parent = parent
    }
    
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
        
    }
    
    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let routePolyline = overlay as? MKPolyline {
            let renderer = MKPolylineRenderer(polyline: routePolyline)
            renderer.strokeColor = UIColor.systemBlue
            renderer.lineWidth = 10
            return renderer
        }
        return MKOverlayRenderer()
    }
}

Solution

  • Whenever you are making a MapKit annotation, you should include func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?. This function allows you to configure your pins, but it also allows you to reuse unused pins. Whenever a pin disappears from a map (scrolling around, etc.), that pin is not destroyed, but it is held to be reused in another pin is needed. This saves processor and memory.

    In your Coordinator class add the following function:

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        // create a unique identifier for pin reuse
        let identifier = "Placemark"
        
        // see if there already is a created pin
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
        
        if annotationView == nil {
            // there wasn't a pin, so we make a new one
            annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            
            // this is where your title is allowed to be shown when tapping the pin
            annotationView?.canShowCallout = true
            
            // this gives you an information button in the callout if needed
            // if you use the rightCalloutAccessoryView you must implement:
            // func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
            annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        } else {
            // we have an old annotation, so update it
            annotationView?.annotation = annotation
        }
        
        return annotationView
    }