Search code examples
iosswiftmapkitmkannotationviewmapkitannotation

Trouble creating custom MKAnnotationView


I'm having a hard time displaying a custom annotation view. Specifically, I'm trying to set an image named "pin" to be the new map pin. The default pin always shows. I've been making small changes for a few hours to no avail, such as changing "pin" to "pin.png" and altering the structure of the mapView:viewFor method. Here's what I have. Could you please take a look and see if anything stands out?

Thanks for any help!

Annotation Class:

class Annotation: NSObject, MKAnnotation {
    dynamic var coordinate : CLLocationCoordinate2D
    var title: String?
    var subtitle: String?

    init(location coord:CLLocationCoordinate2D) {
        self.coordinate = coord
        super.init()
    }

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

AnnotationView Class:

class AnnotationView : MKAnnotationView {
    override init(annotation:MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation,
               reuseIdentifier: reuseIdentifier)
        let im = UIImage(named: "pin")!
        self.frame = CGRect(x: 0, y: 0, width: im.size.width / 3.0 + 5, height: im.size.height / 3.0 + 5)
        self.centerOffset = CGPoint(x: 0, y: -20)
        self.isOpaque = false
    }
    required init (coder: NSCoder) {
        fatalError("NSCoding not supported")
    }
    override func draw(_ rect: CGRect) {
        let im = UIImage(named: "pin")!
       im.draw(in: self.bounds.insetBy(dx: 5, dy: 5))
    }
}

mapView:viewFor: Method:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    var v : MKAnnotationView! = nil
    let ident = "pin"
    v = mapView.dequeueReusableAnnotationView(withIdentifier: ident)
    if v == nil {
        v = AnnotationView(annotation: annotation, reuseIdentifier: ident)
        v.canShowCallout = true
    }
    v.annotation = annotation
    return v
}

Other Relevant Methods:

@IBAction func submitDog(_ sender: Any) {
    let newDog = Dog(name: newDogName.text!, score: 11, picture: image!, location: location!)
    dogs.append(newDog)
    print(dogs.last!)
    UIView.animate(withDuration: 0.5, animations: {

    }) { _ in
        self.newDogView.animation = "slideUp"
        self.newDogView.animate()
        self.newDogView.isHidden = true
        self.newDogName.text = ""
        self.map.isUserInteractionEnabled = true
    }
    dropNewPin(locatedAt: dogs.last!.location, name: dogs.last!.name, rate: dogs.last!.score)
}

func dropNewPin(locatedAt: CLLocation, name: String, rate: Int) {
    let annotation = Annotation(location: CLLocationCoordinate2D(latitude: locatedAt.coordinate.latitude, longitude: locatedAt.coordinate.longitude))
    annotation.title = name
    annotation.subtitle = "\(rate)/10"
    self.map.addAnnotation(annotation)
}

Solution

  • First you need add your viewController as delegate of your map

     self.mapView.delegate = self
    

    After that I recommend you use the MKAnnotationView instead of modify and add the image with custom drawing, if you need a custom Annotation view then you need to add a xib file and your custom class as file owner and make the proper adjustments

     func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        //to avoid make a custom Annotation view for your user location
        if(annotation is MKUserLocation){
            return nil
        }
    
        let ident = "pin"
        var v = mapView.dequeueReusableAnnotationView(withIdentifier: ident)
        if v == nil {
            v = MKAnnotationView(annotation: annotation, reuseIdentifier: ident)
            v?.image = UIImage(named: "pin")
            v?.canShowCallout = true
        }
        v?.annotation = annotation
        return v
    }