How to add different actions to rightCalloutAcessoryView button for each annotation?

Hello,

I've added two buttons for my annotationView. I have 25 annotations and I would like to have seperate button actions for each annotation. Now I have only one button action for every annotation. Here is my code:

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {

    let initialLocation = CLLocation(latitude: 85.10786543576327, longitude: 11.03851472106171)

    let locationManager = CLLocationManager()

    struct Place {

      let name: String

      let description: String

      let type: String

      let lattitude: CLLocationDegrees

      let longtitude: CLLocationDegrees

        var coordinate: CLLocationCoordinate2D {

            .init(latitude: lattitude, longitude: longtitude)

        }

    }

let location = CLLocation()

    let places = [Place(name: "One", description: "One", type: "one", lattitude: 81.108187, longtitude: 12.075812),

                   Place(name: "Two", description: "Two", typ: "two", lattitude: 81.076187, longtitude: 11.000563),

                   Place(name: "Three", description: "Three", typ: "Three", lattitude: 81.076187, longtitude: 11.000563)]

[...]

func findPlace(_ miejsca: [Place]) {
      for place in places {
        let annotations = MKPointAnnotation()
        annotations.title = place.name
        annotations.subtitle = place.description
        annotations.coordinate = CLLocationCoordinate2D(latitude:
          place.lattitude, longitude: place.longtitude)
        mapView.addAnnotation(annotations)
      }
    }

@objc func didClickDetailDisclosure(button: UIButton) {

        guard let vc = storyboard?.instantiateViewController(withIdentifier: "kopuly_vc") as? KopulyController else {

                    return

                }

                present(vc, animated: true)

    }

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

        guard !(annotation is MKUserLocation) else { return nil }

        mapView.delegate = self

        let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: String(annotation.hash))

        let identifier = "identifier"

        annotationView.canShowCallout = true

        guard let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier, for: annotation) as? MKMarkerAnnotationView else { return nil }

        let rightButton = UIButton(type: .detailDisclosure) // HERE IS THIS BUTTON, BUT I NEED EACH BUTTON FOR EACH ANNOTATION - THIS WAY IT SHOULD BE ONE FOR ALL OF THEM

        rightButton.tag = annotation.hash

        annotationView.canShowCallout = true

        annotationView.rightCalloutAccessoryView = rightButton
rightButton.addTarget(self, action: #selector(didClickDetailDisclosure(button:)), for: .touchUpInside)

let leftButton = UIButton(frame: CGRect(

          origin: CGPoint.zero,

          size: CGSize(width: 25, height: 25)))

        leftButton.setBackgroundImage(#imageLiteral(resourceName: "nav"), for: .normal)

        annotationView.leftCalloutAccessoryView = leftButton

        leftButton.addTarget(self, action: #selector(didClickDetailDisclosure(button:)), for: .touchUpInside)

[...]

Thank you in advance for your help

Answered by Claude31 in 692358022

What do you mean by setting a tag to each annotation?

Unfortunately, you cannot add the tag directly.

What I did was to use the title to add this information. Either with an explicit number (at the beginning or at the end). That's what you do if you give title as One, Two… I used a trick to add sequence of invisible characters (space and optionSpace)

You could modify findPlace for this:

func findPlace(_ miejsca: [Place]) {
      for (i, place) in places.enumerated() {
        let annotations = MKPointAnnotation()
        annotations.title = String(i) + "-" + place.name  // initial chars before "-" give the "tag"
        annotations.subtitle = place.description
        annotations.coordinate = CLLocationCoordinate2D(latitude:
          place.lattitude, longitude: place.longtitude)
        mapView.addAnnotation(annotations)
      }
    }

Then, in

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

You retrieve this Int at the beginning to use in the switch case.

You do not show the different didClickDetailDisclosure.

You could have:

// for VC1
@objc func didClickDetailDisclosure1(button: UIButton) {

        guard let vc = storyboard?.instantiateViewController(withIdentifier: "kopuly_vc1") as? KopulyController else { return }
        present(vc, animated: true)
    }

// for VC2
@objc func didClickDetailDisclosure2(button: UIButton) {

        guard let vc = storyboard?.instantiateViewController(withIdentifier: "kopuly_vc2") as? KopulyController else { return }
        present(vc, animated: true)
    }

Hope that's clear.

Did you try to:

  • set a tag for each annotation
  • use it to set the target
  • use different func if needed for left button
switch annotation.tag {
case 0:  rightButton.addTarget(self, action: #selector(didClickDetailDisclosure0(button:)), for: .touchUpInside)
case 1:  rightButton.addTarget(self, action: #selector(didClickDetailDisclosure1(button:)), for: .touchUpInside)
// etc …

You could also define an array of func didClickDetailDisclosure

@Claude31 I did sth like this and it changes the icon, but it does not change the VC:

switch annotation.title!! {

        case "One":

            annotationView.markerTintColor = UIColor.gray

            annotationView.glyphImage = UIImage(named: "one")

        case "Two":

            annotationView.markerTintColor = UIColor.black

            annotationView.glyphImage = UIImage(named: "two")

            rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)

What do you mean by setting a tag to each annotation? Is it impossible to do it this way like icon style? Thank you in advance for your help.

Accepted Answer

What do you mean by setting a tag to each annotation?

Unfortunately, you cannot add the tag directly.

What I did was to use the title to add this information. Either with an explicit number (at the beginning or at the end). That's what you do if you give title as One, Two… I used a trick to add sequence of invisible characters (space and optionSpace)

You could modify findPlace for this:

func findPlace(_ miejsca: [Place]) {
      for (i, place) in places.enumerated() {
        let annotations = MKPointAnnotation()
        annotations.title = String(i) + "-" + place.name  // initial chars before "-" give the "tag"
        annotations.subtitle = place.description
        annotations.coordinate = CLLocationCoordinate2D(latitude:
          place.lattitude, longitude: place.longtitude)
        mapView.addAnnotation(annotations)
      }
    }

Then, in

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

You retrieve this Int at the beginning to use in the switch case.

You do not show the different didClickDetailDisclosure.

You could have:

// for VC1
@objc func didClickDetailDisclosure1(button: UIButton) {

        guard let vc = storyboard?.instantiateViewController(withIdentifier: "kopuly_vc1") as? KopulyController else { return }
        present(vc, animated: true)
    }

// for VC2
@objc func didClickDetailDisclosure2(button: UIButton) {

        guard let vc = storyboard?.instantiateViewController(withIdentifier: "kopuly_vc2") as? KopulyController else { return }
        present(vc, animated: true)
    }

Hope that's clear.

@Claude31 Should I add that kind of switch? If yes it is still showing only first VC:

switch annotation.title!! {

        case "0-One":

            annotationView.markerTintColor = UIColor.gray

            annotationView.glyphImage = UIImage(named: "one")

        case "1-Two":

            annotationView.markerTintColor = UIColor.black

            annotationView.glyphImage = UIImage(named: "white_flag")

            rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)// it should show the second VC but it still shows just this one

When I do this:

 switch annotation.title {

        case 0:

           annotationView.markerTintColor = UIColor.gray

           annotationView.glyphImage = UIImage(named: "kopula")

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure(button:)), for: .touchUpInside)

        case 1:

           annotationView.markerTintColor = UIColor.black

           annotationView.glyphImage = UIImage(named: "white_flag")

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)

            

        }

It shows me Expression pattern of type 'Int' cannot match values of type 'String??'

Did you try:

 switch annotation.title {

        case "0":

But if you know the name of annotations (One, Two…), no need to add a number before. But if the title is dynamic, that will be very useful.

@Claude31 Hi, it only works with the number before like this and it does NOT change the tint color and image :

switch annotation.title {

        case "0-One" :

           annotationView.markerTintColor = UIColor.gray

           annotationView.glyphImage = UIImage(named: "photo")

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure(button:)), for: .touchUpInside)

        case "1-Two":

           annotationView.markerTintColor = UIColor.black

           annotationView.glyphImage = UIImage(named: "photo1")

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)

            

       case .none:

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)

       case .some(_):

           rightButton.addTarget(self, action: #selector(didClickDetailDisclosure2(button:)), for: .touchUpInside)

       }

If I do case "One" without number before it dont get that. Is there any way to start with 1 instead of 0?

How to add different actions to rightCalloutAcessoryView button for each annotation?
 
 
Q