Reputation: 71
I'm using MapKit in Xcode 11.1 with SwiftUI to show a map with annotations. The annotations have a callout which shows the title, and a button on the rightCalloutAccessoryView. I want to be able to navigate to another view when the user clicks the rightCalloutAccessoryView button.
A NavigationLink within the calloutAccessoryControlTapped function isn't working. How should this be done in SwiftUI? Any help is much appreciated!
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationView")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "AnnotationView")
}
let whiteImage = UIImage(named: "white")
annotationView!.image = whiteImage
annotationView?.isEnabled = true
annotationView?.canShowCallout = true
annotationView?.centerOffset = CGPoint(x: 0, y: -23)
let button = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = button
return annotationView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
@State var tapped = true
//ERROR: Property wrappers are not yet supported on local properties
NavigationLink(destination: DetailView(), isActive: $tapped) {EmptyView()}
//ERROR: Use of unrecognized identifier '$tapped'
}
Upvotes: 0
Views: 2135
Reputation: 413
Hope it's not too late but I ran into the same issue. I figured out a workaround by using NavigationLink with isActive:
.
First create @State
variables in where you include your SwiftUI MapView like:
@State var isActive: Bool = false
@State var selectedAnnotation: MKAnnotation?
Then group a NavigationLink with your SwiftUI MapView like this:
Group {
NavigationLink(destination: [relate your selectedAnnotation to your destination view], isActive: self.$isActive) {
EmptyView()
}
MapView(annotations: getAnnotations(), isClicked: self.$isActive, selectedAnnotation: self.$selectedAnnotation)
}
Finally inside your Coordinator/MKMapViewDelegate
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {}
you would want to do something like:
self.view.isClicked = true
self.view.selectedAnnotation = view.annotation
Note that the view
in view.annotation
refers to annotationView view: MKAnnotationView
while self.view
refers to var view: MapView
in
class Coordinator: NSObject, MKMapViewDelegate {
var view: MapView
init(_ control: MapView) {
self.view = control
}
func mapView(_ mapView: MKMapView, viewFor
annotation: MKAnnotation) -> MKAnnotationView? {}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {}
...
}
There's probably a better way to do this but this works!
Note: I created MapView
that conforms to UIViewRepresentable.
Upvotes: 3
Reputation: 6849
There is no mapView(...)
API to do this.
What you do is something like
button.addTarget(self, action: #selector(self.buttonAction(_:)), for: .touchUpInside)
to get the tap from your button.
This is a good API design from Apple, since it gives you the freedom to use arbitrary controls, including container views containing several arbitrary controls.
Think of this problem as "how do I use a UIButton programmatically?". The fact that the UIButton
is used inside MapKit is not very relevant.
Upvotes: 0