confile confile - 3 months ago 21
Swift Question

Customize MKAnnotation Callout View?

I have a


let ann = MKPointAnnotation()
self.ann.coordinate = annLoc
self.ann.title = "Customize me"
self.ann.subtitle = "???"

It looks like this:

enter image description here

How can I customize this callout view to create my own view instead of the predefined one?

Rob Rob

It should first be noted that the simplest changes to the callout are enabled by simply adjusting the properties of the system provided callout, but customizing the right and left accessories (via rightCalloutAccessoryView and leftCalloutAccessoryView). You can do that configuration in viewForAnnotation.

In iOS 9, we now have access to the detailCalloutAccessoryView which, replaces the subtitle of the callout with a potentially visually rich view, while still enjoying the automatic rendition of the callout bubble (using auto layout makes this easier).

For example, here is a callout that used a MKSnapshotter to supply the image for an image view in the detail callout accessory as demonstrated in WWDC 2015 video What's New in MapKit:

enter image description here

You can achieve this with something like:

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    if annotation is MKUserLocation {
        return nil

    let identifier = "MyCustomAnnotation"

    var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
    if annotationView == nil {
        annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView?.canShowCallout = true
    } else {
        annotationView!.annotation = annotation


    return annotationView

func configureDetailView(annotationView: MKAnnotationView) {
    let width = 300
    let height = 200

    let snapshotView = UIView()
    let views = ["snapshotView": snapshotView]
    snapshotView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[snapshotView(300)]", options: [], metrics: nil, views: views))
    snapshotView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[snapshotView(200)]", options: [], metrics: nil, views: views))

    let options = MKMapSnapshotOptions()
    options.size = CGSize(width: width, height: height)
    options.mapType = .SatelliteFlyover = MKMapCamera(lookingAtCenterCoordinate: annotationView.annotation!.coordinate, fromDistance: 250, pitch: 65, heading: 0)

    let snapshotter = MKMapSnapshotter(options: options)
    snapshotter.startWithCompletionHandler { snapshot, error in
        if snapshot != nil {
            let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
            imageView.image = snapshot!.image

    annotationView.detailCalloutAccessoryView = snapshotView

If you're looking for a more radical redesign of the callout or need to support iOS versions prior to 9, it takes more work. The process entails (a) disabling the default callout; and (b) adding your own view when the user taps on the existing annotation view (i.e. the visual pin on the map).

The complexity then comes in the design of the callout, where you have to draw everything you want visible. E.g. if you want to draw a bubble to yield the popover feel of the call out, you have to do that yourself. But with some familiarity with how to draw shapes, images, text, etc., you should be able to render a callout that achieves the desired UX:

enter image description here