Search code examples
iosswiftmkmapviewmkannotationmkannotationview

Swift Annotation CallOut Button selection


I have a mapView with a custom annotation. I also added a button to this but is there a way to see on which annotation the button was pressed?

So the post to the console should not only be "Disclosure Pressed!". I would need something like "Disclosure Pressed! info(X).subtitle" to see the user taped info1 or info2.

Here is the code:

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {

    @IBOutlet weak var Map: MKMapView!

    class CustomPointAnnotation: MKPointAnnotation {
        var imageName: String!
    }

    @IBAction func btpressed(sender: AnyObject) {

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        //1
        var lat1:CLLocationDegrees = 40.748708
        var long1:CLLocationDegrees = -73.985643
        var latDelta1:CLLocationDegrees = 0.01
        var longDelta1:CLLocationDegrees = 0.01

        var span1:MKCoordinateSpan = MKCoordinateSpanMake(latDelta1, longDelta1)
        var location1:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat1, long1)
        var region1:MKCoordinateRegion = MKCoordinateRegionMake(location1, span1)

        Map.setRegion(region1, animated: true)

        var info1 = CustomPointAnnotation()
        info1.coordinate = location1
        info1.title = "Test Title1!"
        info1.subtitle = "Subtitle1"
        info1.imageName = "1.png"

        Map.addAnnotation(info1)

        //2
        var lat2:CLLocationDegrees = 41.748708
        var long2:CLLocationDegrees = -72.985643
        var latDelta2:CLLocationDegrees = 0.01
        var longDelta2:CLLocationDegrees = 0.01

        var span2:MKCoordinateSpan = MKCoordinateSpanMake(latDelta2, longDelta2)
        var location2:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat2, long2)
        var region2:MKCoordinateRegion = MKCoordinateRegionMake(location2, span2)

        var info2 = CustomPointAnnotation()
        info2.coordinate = location2
        info2.title = "Test Title2!"
        info2.subtitle = "Subtitle2"
        info2.imageName = "2.png"
        Map.addAnnotation(info2)
    }

    func mapView(mapView: MKMapView!, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {

        if control == annotationView.rightCalloutAccessoryView {
            println("Disclosure Pressed!")
        }
    }

    func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
        if !(annotation is CustomPointAnnotation) {
            return nil
        }

        let reuseId = "test"

        var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
        if anView == nil {
            anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
            anView.canShowCallout = true

            anView.rightCalloutAccessoryView = UIButton.buttonWithType(.InfoDark) as UIButton

            var imageview = UIImageView(frame: CGRectMake(0, 0, 10, 10))
            imageview.image = UIImage(named: "1.png")
            anView!.leftCalloutAccessoryView = imageview
        } else {
            anView.annotation = annotation
        }

        //Set annotation-specific properties **AFTER**
        //the view is dequeued or created...

        let cpa = annotation as CustomPointAnnotation
        anView.image = UIImage(named:cpa.imageName)

        return anView
    }

}

I've already tried something like:

if control == annotationView.rightCalloutAccessoryView {
    println("Disclosure Pressed!" \(self.subtitle))
}

Solution

  • In the calloutAccessoryControlTapped delegate method, self is not the annotation whose callout accessory view was tapped.

    self refers to the instance of the class the method is contained in (i.e. an instance of ViewController). The other issue is that in the println, you put the variable reference outside the quotes instead of inside.


    The delegate method gives you a reference to the annotation view: the annotationView argument.

    The annotation view contains a reference to the annotation: annotationView.annotation.


    However, please note your delegate method declaration differs slightly from the documented one (annotationView argument has a local name view and other arguments are marked as optional with the ! suffix).

    It's better to use the documented declaration even though your version will technically still work.

    Complete example below also shows how to check if the annotation is one of your custom objects and how to access the custom property:

    func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, 
                 calloutAccessoryControlTapped control: UIControl!) {
    
        if control == view.rightCalloutAccessoryView {
            println("Disclosure Pressed! \(view.annotation.subtitle)")
    
            if let cpa = view.annotation as? CustomPointAnnotation {
                println("cpa.imageName = \(cpa.imageName)")
            }
        }
    
    }
    


    Side note:
    Since you've set the leftCalloutAccessoryView to a UIImageView, tapping on it will not call calloutAccessoryControlTapped because it is only called for views that are subclasses of UIControl. UIImageView is not a subclass of UIControl. If you need to detect taps on the left accessory, create a custom UIButton (and set its image) instead of a UIImageView.