I'm using UIRepresentable to show annotations on a map, and want to be able to show a view when tapping on the selected pin.
I was previously using Map()
so was able to use the .onTapGesture
for the annotations, but now that the annotations are made from UIKit, how to I pass the selected item to the main view?
What I previously had working:
var body: some View {
ZStack {
Map(region: $region, annotationItems: $model.locations) { location in
MapPin(coordinate: location.coord)
.onTapGesture {
modelData.selectedLocation = location
modelData.isShowingDetail = true
}
}
if modelData.isShowingDetail {
DetailView(
isShowingDetail: $modelData.isShowingDetail,
location: modelData.selectedLocation!
)
}
}
}
Now I have the UIViewRepresentable:
struct UIMapView: UIViewRepresentable {
// default setup - coordinator, makeUI, updateUI
class Coordinator: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
// how to trigger the overlay??
}
}
}
Any help would be appreciated as I am very stuck on this :)
You want to know the selected annotation in your SwiftUI view. So you have to store it somewhere. Declare a @State
:
struct ContentView: View {
let locations: [MKAnnotation]
@State private var selectedLocation: MKAnnotation?
var body: some View {
// ... //
}
}
Now in your wrapper (UIViewRepresentable
) you have to make a binding with this MKAnnotation?
:
struct MapView: UIViewRepresentable {
@Binding var selectedLocation: MKAnnotation? // HERE
let annotations: [MKAnnotation]
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.region = // .... //
mapView.addAnnotations(annotations)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {
// .... //
}
Now you should be able to access this variable in your Delegate (Coordinator). For that you have to pass the UIViewRepresentable to the Coordinator:
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapView
init(_ parent: MapView) {
self.parent = parent
}
And finally in func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)
you can copy the MKAnnotation in parent.selectedLocation
.
With the @Binding
this MKAnnotation
is now accessible in your parent view (ContentView
). You can display its properties in your DetailView
.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// ... //
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
parent.selectedLocation = view.annotation
}
}
}
For example :
struct ContentView: View {
let locations: [MKAnnotation]
@State private var selectedLocation: MKAnnotation?
var body: some View {
VStack {
Text("\(selectedLocation?.coordinate.latitude ?? 0)")
// Don't forget the Binding : $selectedLocation
MapView(selectedLocation: $selectedLocation, annotations: locations)
}
}
}