How to make a custom MKAnnotationView with XIB

白昼怎懂夜的黑 提交于 2020-01-14 01:36:07

问题


I want to have a custom MKAnnotationView. I've created a xib file in IB and set its class to MyAnnotationView.

    class MyAnnotationView: MKAnnotationView {

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

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

    @IBOutlet weak var textLabel: UILabel!
    @IBOutlet weak var busIcon: UIImageView!

}

Here's how the xib looks like - it has a textLabel and a busIcon linked:

I'm using the viewFor annotation delegate method to create views for all annotations:

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

        // Don't want to show a custom image if the annotation is the user's location.
        if (annotation is MKUserLocation) {
            return nil
        } else {

            let annotationIdentifier = "AnnotationIdentifier"
            var annotationView: MyAnnotationView?                           


            if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MyAnnotationView {
                annotationView = dequeuedAnnotationView
                annotationView?.annotation = annotation
            } else {

                // if no views to dequeue, create an Annotation View
                let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
                av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                annotationView = av     
            }


            if let annotationView = annotationView {
                annotationView.canShowCallout = true                        // callout bubble
                annotationView.image = UIImage(named: "Delivery")
                annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
            }

            return annotationView

        }

    }

The annotationView.image = UIImage(named: "Delivery")

&

AnnotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)

are there just to check if the code is working and display a sample view on the map, as they use the standard properties inherited from MKAnnotationView.

I don't know how to make the viewFor annotation method use the XIB I have created. Could anyone please help me with that? I searched for the solution, but only found something relevant in Obj C.

Thank you!


回答1:


1- Create a view subclass of UIView with xib say CallView

2- Inside viewforAnnotation

let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "id") 
let customView = Bundle.main.loadNibNamed("CallView", owner: self, options: nil).first! as! CallView
// here configure label and imageView
annotationView.addSubview(customView)



回答2:


UPDATED CODE BASED ON Sh-Khan's answer

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

                //  Don't want to show a custom image if the annotation is the user's location.
                if (annotation is MKUserLocation) {
                    return nil
                } else {

                    let annotationIdentifier = "AnnotationIdentifier"
                    let nibName = "MyAnnotationView"
                    let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView
                    var annotationView: MyAnnotationView?

                    // if there is a view to be dequeued, use it for the annotation
                    if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {

                        if dequeuedAnnotationView.subviews.isEmpty {
                            dequeuedAnnotationView.addSubview(viewFromNib)
                        }
                        annotationView = dequeuedAnnotationView
                        annotationView?.annotation = annotation
                    } else {

                        // if no views to dequeue, create an Annotation View
                        let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
                        av.addSubview(viewFromNib)
                        annotationView = av     // extend scope to be able to return at the end of the func
                    }

                    // after we manage to create or dequeue the av, configure it
                    if let annotationView = annotationView {
                        annotationView.canShowCallout = true                                    // callout bubble
                        annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                        annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)

                        let customView = annotationView.subviews.first as! MyAnnotationView
                        customView.frame = annotationView.frame
                        customView.textLabel.text = (annotationView.annotation?.title)!
                    }
                    return annotationView
                }
}



回答3:


**Create Custom MKPointAnnotation Class**

import UIKit
import MapKit

    class CustomPointAnnotation: MKPointAnnotation {
       var id : Int
       var url : String

      init(id : Int , url : String ) {
      self.id = id
       self.url = url

}
    }

Create Xib for MarkerView Class for Annotation View

class MarkerView: MKAnnotationView {
    @IBOutlet weak var imgVwUser: UIImageView!



     init(annotation: CustomPointAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        if (hitView != nil)
        {
            self.superview?.bringSubviewToFront(self)
        }
        return hitView
    }
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        let rect = self.bounds
        var isInside: Bool = rect.contains(point)
        if(!isInside)
        {
            for view in self.subviews
            {
                isInside = view.frame.contains(point)
                if isInside
                {
                    break
                }
            }
        }
        return isInside
    }
}

Add MKMapView Delegates In Your ViewController

extension YourViewController : MKMapViewDelegate {

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

        //  Don't want to show a custom image if the annotation is the user's location.
        if (annotation is MKUserLocation) {
            return nil
        } else {

            let annotationIdentifier = "AnnotationIdentifier"
            let nibName = "MarkerView"
            let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MarkerView
            var annotationView: MarkerView?

            // if there is a view to be dequeued, use it for the annotation
            if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MarkerView {

                if dequeuedAnnotationView.subviews.isEmpty {
                    dequeuedAnnotationView.addSubview(viewFromNib)
                }
                annotationView = dequeuedAnnotationView
                annotationView?.annotation = annotation
            } else {

                // if no views to dequeue, create an Annotation View
                let av = MarkerView(annotation: annotation as? CustomPointAnnotation, reuseIdentifier: annotationIdentifier)
                av.addSubview(viewFromNib)
                annotationView = av     // extend scope to be able to return at the end of the func
            }

            // after we manage to create or dequeue the av, configure it
            if let annotationView = annotationView {
                annotationView.canShowCallout = false                                    // callout bubble


                if let annotation =  annotation as? CustomPointAnnotation {


                    annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                    annotationView.frame = CGRect(x: 0, y: 0, width: 66, height: 75)

                    let customView = annotationView.subviews.first as? MarkerView
                    customView?.frame = annotationView.frame
                    let image = annotation.url

                    let imageUrl = URL(string: image)


                    customView?.imgVwUser.sd_setImage(with: imageUrl, placeholderImage:  UIImage(named:"defaults"), options: [.refreshCached], completed: nil)


                }
            }

            return annotationView
        }
    }




     }

ADD Annotation to the mapview

extension YourViewController  {

    func addAnnotation(){

        let annotationsToRemove = mapView.annotations.filter { $0 !== mapView.userLocation }
        mapView.removeAnnotations( annotationsToRemove )

        var annotations: [CustomPointAnnotation] = []

        for  i in 0..<self.arrayData.count {


            let customPoints = CustomPointAnnotation.init(id: arrayData[i].id  ?? 0, url:  arrayData[i].url)


            let location = CLLocationCoordinate2DMake(self.arrayData[i].lat ?? 0, self.arrayData[i].lng ?? 0)
            customPoints.coordinate = location

            annotations.append(customPoints)
        }

        mapView.addAnnotations(annotations)

    }


}


来源:https://stackoverflow.com/questions/53840748/how-to-make-a-custom-mkannotationview-with-xib

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!