30

I managed to get a custom icon for a annotation pin in Swift, but now I am still stuck using 2 different images for different annotations. Right now a button adds a annotation to the map. There should be another button that also adds a annotation but with another icon.

Is there a way to use the reuseId for this?

class ViewController: UIViewController, MKMapViewDelegate {

@IBOutlet weak var Map: MKMapView!

@IBAction func btpressed(sender: AnyObject) {

    var lat:CLLocationDegrees = 40.748708
    var long:CLLocationDegrees = -73.985643
    var latDelta:CLLocationDegrees = 0.01
    var longDelta:CLLocationDegrees = 0.01

    var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
    var location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, long)
    var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)

    Map.setRegion(region, animated: true)


    var information = MKPointAnnotation()
    information.coordinate = location
    information.title = "Test Title!"
    information.subtitle = "Subtitle"

    Map.addAnnotation(information)
}

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

    let reuseId = "test"

    var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
    if anView == nil {
        anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
        anView.image = UIImage(named:"1.png")
        anView.canShowCallout = true
    }
    else {
        anView.annotation = annotation
    }

    return anView
}
Fabian Boulegue
  • 6,476
  • 14
  • 47
  • 72

2 Answers2

83

In the viewForAnnotation delegate method, set the image based on which annotation the method is being called for.

Be sure to do this after the view is dequeued or created (and not only in the if anView == nil part). Otherwise, annotations that use a dequeued view will show the image of the annotation that used the view previously.

With the basic MKPointAnnotation, one crude way to tell annotations apart is by their title but that's not very flexible.

A better approach is to use a custom annotation class that implements the MKAnnotation protocol (an easy way to do that is to subclass MKPointAnnotation) and add whatever properties are needed to help implement the custom logic.

In the custom class, add a property, say imageName, which you can use to customize the image based on the annotation.

This example subclasses MKPointAnnotation:

class CustomPointAnnotation: MKPointAnnotation {
    var imageName: String!
}

Create annotations of type CustomPointAnnotation and set their imageName:

var info1 = CustomPointAnnotation()
info1.coordinate = CLLocationCoordinate2DMake(42, -84)
info1.title = "Info1"
info1.subtitle = "Subtitle"
info1.imageName = "1.png"

var info2 = CustomPointAnnotation()
info2.coordinate = CLLocationCoordinate2DMake(32, -95)
info2.title = "Info2"
info2.subtitle = "Subtitle"
info2.imageName = "2.png"

In viewForAnnotation, use the imageName property to set the view's image:

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
    }
    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
}
  • Simple and straightforward +1 – Mohamed Said Mar 31 '15 at 09:15
  • 5
    Just a note that you need to implement the protocol 'MKMapViewDelegate' and configure your controller to be your delegate (either in the storyboard or manually in the code) for the `func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! ` method to work. This tripped me up for a bit. – Lindsay Thurmond Apr 06 '15 at 17:00
  • the best answer i found for this question. +1 for the order and the simplicity of the answer – MBH May 25 '15 at 20:17
  • @Anna my pin points are not displaying on the map. only callout is there. whats wrong – Qadir Hussain May 28 '15 at 13:43
  • Hi @Anna, My all points are not covered in my UImapview, it is going out of bounds... What should be the case? – dhaval shah Jun 23 '15 at 09:44
  • @Anna how would I do this to display a different callout view for each of the 3 annotation types I have? Thanks – Trip Phillips Sep 13 '15 at 21:28
  • fatal error: unexpectedly found nil while unwrapping an Optional value . I'm getting this error at `let cpa = annotation as! CustomPointAnnotation ` – Sabhay Sardana Aug 01 '16 at 05:21
  • i am use this but it is also show default pin image. – Nipul Daki Sep 28 '17 at 12:11
13

iOS Swift Code With Help of Anna and Fabian Boulegue:

import UIKit
import MapKit

class ViewController: UIViewController, MKMapViewDelegate {

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.mapView.delegate = self

        var info1 = CustomPointAnnotation()
        info1.coordinate = CLLocationCoordinate2DMake(26.889281, 75.836042)
        info1.title = "Info1"
        info1.subtitle = "Subtitle"
        info1.imageName = "flag.png"

        var info2 = CustomPointAnnotation()
        info2.coordinate = CLLocationCoordinate2DMake(26.862280, 75.815098)
        info2.title = "Info2"
        info2.subtitle = "Subtitle"
        info2.imageName = "flag.png"

        mapView.addAnnotation(info1)
        mapView.addAnnotation(info2)
    }

    func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {

        println("delegate called")

        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
        }
        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
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

class CustomPointAnnotation: MKPointAnnotation {
    var imageName: String!
}
Bista
  • 7,869
  • 3
  • 27
  • 55
Vinod Joshi
  • 7,696
  • 1
  • 50
  • 51
  • for Swift 3 I had to change to use: `func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {` – Alien Feb 19 '17 at 23:53
  • Need to downcast the annotation to CustomPointAnnotation. Change it to `let pickupCustomAnno = annotation as! CustomPointAnnotation` – Vinoth Vino Mar 27 '19 at 07:07
  • would that be bad to leave the reuseId as "test" ? what would that bring ? – Cublax Sep 12 '20 at 11:45