I want to draw over a map while walking on the street to record my path in SwiftUI.
I have a locationManager:
class LocationManager: NSObject, ObservableObject {
let locationManager = CLLocationManager()
let geoCoder = CLGeocoder()
@Published var knownLocations: [CLLocation] = []
@Published var location: CLLocation?
@Published var placemark: CLPlacemark?
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
func geoCode(with location: CLLocation) {
geoCoder.reverseGeocodeLocation(location) { (placemark, error) in
if error != nil {
print(error!.localizedDescription)
} else {
self.placemark = placemark?.first
}
}
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.first else { return }
knownLocations.append(location)
DispatchQueue.main.async {
self.location = location
self.geoCode(with: location)
}
}
}
That contains an array of knownLocations, every time I move the didUpdateLocations is triggered correctly and saves the coordinates into the array.
My ContentView looks like this:
struct ContentView: View {
@ObservedObject var locationManager: LocationManager = LocationManager()
var body: some View {
MapView(knowLocations: locationManager.knownLocations)
.edgesIgnoringSafeArea(.all)
}
}
Which works fine, every time a new coordinate is added, this is called and it creates a new MapView with the added coordinate.
And finally, this is how my MapView looks:
struct MapView: UIViewRepresentable {
let mapView = MKMapView()
let knowLocations: [CLLocation]
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
addOverlays()
return mapView
}
func updateUIView(_ view: MKMapView, context: UIViewRepresentableContext<MapView>) {
addOverlays()
mapView.delegate = context.coordinator
}
private func addOverlays() {
let p = MKPolygon(coordinates: knowLocations.map({$0.coordinate}), count: knowLocations.count)
mapView.addOverlay(p)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
super.init()
}
func mapView(_: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
return renderer
}
}
}
When a new MapView is created with the array, it calls addOverlays() which creates a polygon with all the coordinates. I have checked and until here it's all correctly. The first time addOverlays() its called, the renderFor overlay is also called correctly but after that it stops being called. This means that no path is created. If I mocked knowLocations and at the beginning it contains some fake locations, the path is drawed correctly.
What am I doing wrong? Can I only call renderFor overlay once? Should I just do it every X minutes rather than live?