I’m at the moment utilizing a MapViewRepresentable in SwiftUI, which conforms to UIViewRepresentable and makes use of an MKMapView to show annotations on a map. The MapViewRepresentable takes a Binding for mapRegion, which is initially set to a default worth.
Nonetheless, I observed that if I work together with the map (e.g., pan, zoom), the mapRegion binding in my ViewModel does not get up to date routinely. To resolve this, I applied the mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) technique within the Coordinator, updating the mapRegion binding with the present map’s area. This is the code I am utilizing:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
DispatchQueue.essential.async {
self.mum or dad.mapRegion = mapView.area
} }
This resolution works as anticipated, however I noticed that the view appears to replace itself a number of instances throughout this course of. The annotation animations triggered by the area change happen thrice, which gives the look of a number of state modifications.
I want to know if there’s a higher method to maintain my mapRegion binding up to date with out triggering a number of pointless view updates. Is there a extra environment friendly and simple option to obtain this? Any insights or options are enormously appreciated. Thanks!
That is the UIViewRepresentable:
struct MapViewRepresentable: UIViewRepresentable {
@Binding var mapRegion: MKCoordinateRegion
var locations: [Place]
var onTap: (String) -> Void = { _ in }
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
mapView.showsUserLocation = true
mapView.showsCompass = true
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.setRegion(mapRegion, animated: true)
updateAnnotations(from: uiView)
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
non-public func updateAnnotations(from mapView: MKMapView) {
mapView.removeAnnotations(mapView.annotations)
mapView.addAnnotations(locations)
}
class Coordinator: NSObject, MKMapViewDelegate {
var mum or dad: MapViewRepresentable
init(_ mum or dad: MapViewRepresentable) {
self.mum or dad = mum or dad
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let placeAnnotation = annotation as? Place else {
return nil
}
let annotationView = MKMarkerAnnotationView(annotation: placeAnnotation, reuseIdentifier: "placeAnnotation")
annotationView.canShowCallout = false
annotationView.animatesWhenAdded = true
annotationView.markerTintColor = placeAnnotation.chosen ? UIColor(named: "TripBlue") : Colour(hex: placeAnnotation.coloration)?.uiColor
annotationView.glyphImage = UIImage(systemName: placeAnnotation.icon)
annotationView.glyphTintColor = .white
return annotationView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped management: UIControl) {
if let _ = view.annotation as? Place {
}
}
func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
guard let placeAnnotation = annotation as? Place else {
return
}
self.mum or dad.onTap(placeAnnotation.id ?? "")
}
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
DispatchQueue.essential.async {
self.mum or dad.mapRegion = mapView.area
}
}
}}
This id the use on SwiftUIView
@ObservedObject var exploreMapViewModel: ExploreMapViewModel = ExploreMapViewModel()
@ViewBuilder func generateMap() -> some View {
MapViewRepresentable(mapRegion: $exploreMapViewModel.mapRegion, locations: exploreMapViewModel.annotationList, onTap: { id in
withAnimation {
exploreMapViewModel.selectPlace(id: id, zoom: exploreMapViewModel.mapRegion.span.longitudeDelta)
exploreMapViewModel.scrollTo(id: id)
}
})
}
That is the declaration in ViewModel
@Revealed var mapRegion = MKCoordinateRegion(middle: CLLocationCoordinate2D(latitude: 4.676490, longitude: -74.086259), span: MKCoordinateSpan(latitudeDelta: 0.03, longitudeDelta: 0.03))
I actually recognize your assist. Thanks