Swift - Custom MKAnnotationView, set label title

18,486

Make your custom annotation view

There is no public API allowing you to access the label in the pop up directly. What you need to do is make a subclass of MKPinAnnotationView and do whatever customization you want there. As an example,

class CustomAnnotationView : MKPinAnnotationView
{
    let selectedLabel:UILabel = UILabel.init(frame:CGRectMake(0, 0, 140, 38))

    override func setSelected(selected: Bool, animated: Bool)
    {
        super.setSelected(false, animated: animated)

        if(selected)
        {
            // Do customization, for example:
            selectedLabel.text = "Hello World!!"
            selectedLabel.textAlignment = .Center
            selectedLabel.font = UIFont.init(name: "HelveticaBold", size: 15)
            selectedLabel.backgroundColor = UIColor.lightGrayColor()
            selectedLabel.layer.borderColor = UIColor.darkGrayColor().CGColor
            selectedLabel.layer.borderWidth = 2
            selectedLabel.layer.cornerRadius = 5
            selectedLabel.layer.masksToBounds = true

            selectedLabel.center.x = 0.5 * self.frame.size.width;
            selectedLabel.center.y = -0.5 * selectedLabel.frame.height;
            self.addSubview(selectedLabel)
        }
        else
        {
            selectedLabel.removeFromSuperview()
        }
    }
}    

enter image description here

Other Notes

  1. Use this custom view in the map view:

    func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
        var anno = mapView.dequeueReusableAnnotationViewWithIdentifier("Anno")
        if anno == nil
        {
            anno = CustomAnnotationView.init(annotation: annotation, reuseIdentifier: "Anno")
        }
        return anno;
    }
    
  2. Since the title property of the annotation is not set, you will have to call the map view function selectAnnotation yourself. Add the following to the CustomAnnotationView class:

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        mapView?.selectAnnotation(self.annotation!, animated: true)
    }
    

If you want to have more than one marker on the map:

Usually just draw the annotation simply during initialization. In setSelected just return false (meaning "show all annotations all the time").

class DotAnnotationView : MKPinAnnotationView {

    let dot: UILabel = UILabel.init(frame:CGRect(x: 0, y: 0, width: 20, height: 20))

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        _setup()
    }

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        _setup()
    }

    override func prepareForReuse() {
        dot.text = "you forgot to set the text value?"
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(false, animated: animated)
    }

    func _setup() {
        dot.textAlignment = .center
        .. etc
    }

}

You set the string (or other values - say color of the panel) for each annotation in mapView#viewFor. It's like populating a cell in a UITableView.

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

    let textForThisItem = annotation.title!!
    // or, just use index#of to determine which row this is in your data array
    if annotation.isEqual(mkMap.userLocation) {
        // skip the user-position indicator
        return nil
    }

    var anno = mapView.dequeueReusableAnnotationView(withIdentifier: "anno")

    if anno == nil {
        anno = DotAnnotationView.init(annotation: annotation, reuseIdentifier: "anno")
    }

    (anno as! DotAnnotationView).dot.text = textForThisItem

    return anno
}

Finally note that somewhat confusingly, if you very simply change the class of CustomAnnotationView from MKPinAnnotationView to MKAnnotationView, everything works the same but it replaces "all of the pin" rather than just the annotation.

Share:
18,486
TimWhiting
Author by

TimWhiting

Updated on June 04, 2022

Comments

  • TimWhiting
    TimWhiting almost 2 years

    I am trying to customise the MKAnnotationView for my mapView callout bubbles. I am fine with setting the annotation title when the annotation is created, and also customising the MKAnnotationView to add labels or images e.t.c (in the viewForAnnotation delegate), but how do I change the label created in the viewForAnnotation delegate, so that the title of it is different for each pin?

    The other issue I have is that if I don't add a title or subtitle to the annotation when it is created in the viewDidLoad method, but I still try and create one by leaving self.map.addAnnotation(annotation), when I run the app and tap the pin no callout bubble is displayed.

    In the end I would like to have totally customised callout bubbles, with individual labels on them for each pin. So what i really ned to know is how to access the viewForAnnotation delegate when the annotation is created to change properties of it for each pin.

    override func viewDidLoad() {
            super.viewDidLoad()
    
            var countries: [String] = ["Germany","Germany","Poland","Denmark"]
            var practiceRoute: [CLLocationCoordinate2D] = [CLLocationCoordinate2DMake(50, 10),CLLocationCoordinate2DMake(52, 9),CLLocationCoordinate2DMake(53, 20),CLLocationCoordinate2DMake(56, 14)]
    
            for vari=0; i<practiceRoute.count; i++ {
    
                var annotation = MKPointAnnotation
                annotation.title = countries[i]
                annotation.coordinate = practiceRoute[i]
                self.map.addAnnotation(annotation)
    
            }
    
    }
    
    func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    
        if annotation is MKUserLocation {
            return nil
        }
    
        let reuseId = "pin"
        var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
    
        if(pinView==nil){
    
            pinView=MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
            pinView!.canShowCallout = true
    
            let base = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
            base.backgroundColor = UIColor.lightGrayColor()
    
            let label1 = UILabel(frame: CGRect(x: 30, y: 10, width: 60, height: 15))
            label1.textColor = UIColor.blackColor()
            label1.text = "12 photos"
            base.addSubview(label1)
            pinView!.leftCalloutAccessoryView = base
            pinView!.pinColor = .Red
    
        }
    
        return pinView
    
    }