简体   繁体   中英

How to navigate from mapkit annotation - SwiftUI/Xcode 11

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'

}

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM