Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
474 views
in Technique[技术] by (71.8m points)

ios - Swift - Custom MKAnnotationView, set label title

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

}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...