-1

I want to add a button that is going to appear on the right of the annotation when a user tapped on a marker. Here's currently what I'm doing.

  1. Download a json file from my website.
  2. Open the file and parse it.
  3. Plot all the markers using coords from the json file
  4. But now..how do I add a button to the right of my annotation that will move to a detailViewController when a user tap it?

here's my ViewDidLoad

//Retrieve the existing JSON from document directory
        let defaults = UserDefaults.standard
        let fileUrl = defaults.url(forKey: "pathForJSON")
        do {
            let jsonData = try Data(contentsOf: fileUrl!, options: [])
            let myJson = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [Dictionary<String,AnyObject>]
            // print out the content
//            print("My JSON: ", myJson)
            createAnnotation(locations: myJson)
        } catch {
            print(error)
        }

My createAnnotation function.

func createAnnotation(locations: [Dictionary<String, AnyObject>]) {
        for location in locations {
            let annotation = MKPointAnnotation()
            annotation.title = location["name"] as? String
            let lat = (location["latitude"] as! NSString).doubleValue
            let lon = (location["longitude"] as! NSString).doubleValue
            annotation.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lon)
            mapView.addAnnotation(annotation)
//            print(annotation)
        }
    }
Elm
  • 19
  • 2
  • 1
    Possible duplicate of [How to add button to MKPointAnnotation in Swift](https://stackoverflow.com/questions/41495956/how-to-add-button-to-mkpointannotation-in-swift) – El Tomato Jul 18 '19 at 02:37

2 Answers2

0

Try the following code

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

    let reuseId = "pin"
    var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
    if pinView == nil {
        pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        pinView?.canShowCallout = true

        let rightButton: AnyObject! = UIButton(type: UIButton.ButtonType.detailDisclosure)
        pinView?.rightCalloutAccessoryView = rightButton as? UIView
    }
    else {
        pinView?.annotation = annotation
    }

    return pinView
}

Don't forget to set mapView's delegate

mapView.delegate = self
Kosuke Ogawa
  • 7,383
  • 3
  • 31
  • 52
  • AH! Thank you. I've tried following the above thread mentioned by El tomato but one important thing that I forgot is to set the ViewController as the delegate for mapView! – Elm Jul 19 '19 at 01:32
  • Probably needless to say, but that `AnyObject` and `as? UIView` stuff is unnecessary. Just `pinView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)` is sufficient. – Rob Jul 19 '19 at 05:11
0

By the way, if targeting iOS 11 or later, you can simplify this further, notably:

  • remove your mapView(_:viewFor:) entirely;

  • register a default annotation view class;

  • define an annotation view class that configures the annotation and its callout as desired.

For example:

class CustomAnnotationView: MKPinAnnotationView {
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

        canShowCallout = true
        rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

And, in your view controller:

override func viewDidLoad() {
    super.viewDidLoad()

    mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044