1

I am having an issue with my Coordinator. I am interacting with a MKMapView via SwiftUI. I am passing in a Binding to the UIViewRepresentable and need to access that same Binding in the Coordinator. Inside the Coordinator I determine what strokeColor to use for my polyline. When I try to access the routes Binding from my Coordinator it is always empty. When I set a breakpoint inside the MapView on the updateUIView function the binding is indeed populated.

Heres the code:

import SwiftUI
import MapKit

struct MapView: UIViewRepresentable {
    var region: MKCoordinateRegion
    @Binding var routes: [RouteData]
    
    func makeUIView(context: Context) -> MKMapView {
        let mapView = MKMapView()
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        mapView.setRegion(region, animated: true)
        
        addOverlays(mapView)
        
        return mapView
    }
    
    func updateUIView(_ view: MKMapView, context: Context) {
        addOverlays(view)
        removeOverlays(view)
    }
    
    private func addOverlays(_ view: MKMapView) {
        for route in routes {
            for point in route.points {
                let waypoints = point.waypoints
                let polyline = MKPolyline(coordinates: waypoints, count: waypoints.count)
                polyline.title = route.routeID
                view.addOverlay(polyline)
            }
        }
    }
    
    private func removeOverlays(_ view: MKMapView) {
        for overlay in view.overlays {
            if let routeID = overlay.title!, routes.first(where: { $0.routeID == routeID }) == nil {
                view.removeOverlay(overlay)
            }
        }
    }
    
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

class Coordinator: NSObject, MKMapViewDelegate {
    let parent: MapView
    
    init(_ parent: MapView) {
        self.parent = parent
    }

    func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
        if let routePolyline = overlay as? MKPolyline {
            let renderer = MKPolylineRenderer(polyline: routePolyline)
            
            // Always prints route is empty even though I set a break point inside the parents' updateUIView func and the route is populated.
            print("parents routes: \(self.parent.routes)")
            if let title = routePolyline.title, let route = self.parent.routes.first(where: { $0.routeID == title }) {
                renderer.strokeColor = UIColor(convertRGBStringToColor(color: route.route.rtclr))
            } else {
                renderer.strokeColor = UIColor.blue
            }
            
            renderer.lineWidth = 5
            return renderer
        }
        
        return MKOverlayRenderer()
    }
}

Obito
  • 79
  • 9

1 Answers1

0

A few mistakes

@Binding var routes: [RouteData] should be let routes: [RouteData] because you don’t change it so don’t need the write access.

Coordinator(self) Should be Coordinator(), self is an old value the Coordinator should not hang on to.

Subclass MKPolyline to add your colour properties eg https://stackoverflow.com/a/44294417/259521

makeUIView Should return context.coordinator.mapView

addOverlays should only add ones that are not already added. You need to essentially implement a diff in updateUIView.

Update is called after make so no need to add overlays in make.

malhal
  • 26,330
  • 7
  • 115
  • 133