I tried to make the callout work but that didn't happen as I did something wrong in my prepare for segue. I want to know how to be able to make a pin annotation callout to another view?
问题:
回答1:
The process of segueing to another scene when the button in the callout is tapped is like so:
Set the
delegate
of the map view to be the view controller. You can do this either in Interface Builder's "Connections Inspector" or programmatically. You want to specify that the view controller conforms toMKMapViewDelegate
, too.When you create the annotation, make sure to set the title, too:
let annotation = MKPointAnnotation() annotation.coordinate = coordinate annotation.title = ... mapView.addAnnotation(annotation)
Define an annotation view subclass with callout with a button:
class CustomAnnotationView: MKPinAnnotationView { // or nowadays, you might use MKMarkerAnnotationView override init(annotation: MKAnnotation?, reuseIdentifier: String?) { super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) canShowCallout = true rightCalloutAccessoryView = UIButton(type: .infoLight) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } }
Instruct your
MKMapView
to use this annotation view. iOS 11 has simplified that process, but I’ll describe how to do it both ways:If your minimum iOS version is 11 (or later), you’d just register the custom annotation view in as the default and you’re done. You generally don't implement
mapView(_:viewFor:)
at all in iOS 11 and later. (The only time you might implement that method is if you needed to register multiple reuse identifiers because you had multiple types of custom annotation types.)override func viewDidLoad() { super.viewDidLoad() mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) }
If you need to support iOS versions prior to 11, you would make sure to specify your view controller as the delegate for the
MKMapView
and then would implementmapView(_:viewFor:)
:extension ViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if annotation is MKUserLocation { return nil } let reuseIdentifier = "..." var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) if annotationView == nil { annotationView = CustomAnnotationView(annotation: annotation, reuseIdentifier: reuseIdentifier) } else { annotationView?.annotation = annotation } return annotationView } }
For example, that yields a callout something that looks like the following, with the
.infoLight
button on the right:Implement
calloutAccessoryControlTapped
that programmatically performs the segue:func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { performSegue(withIdentifier: "SegueToSecondViewController", sender: view) }
Obviously, this assumes that you've defined a segue between the two view controllers.
When you segue, pass the necessary information to the destination scene. For example, you might pass a reference to the annotation:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if let destination = segue.destination as? SecondViewController, let annotationView = sender as? MKPinAnnotationView { destination.annotation = annotationView.annotation as? MKPointAnnotation } }
For more information, see Creating Callouts in the Location and Maps Programming Guide.
For Swift 2 implementation of the above, see previous revision of this answer.