如何使用 XIB 制作自定义 MKAnnotationView

Posted

技术标签:

【中文标题】如何使用 XIB 制作自定义 MKAnnotationView【英文标题】:How to make a custom MKAnnotationView with XIB 【发布时间】:2018-12-18 20:42:07 【问题描述】:

我想要一个自定义的 MKAnnotationView。我在 IB 中创建了一个 xib 文件并将其类设置为 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!


这是 xib 的样子 - 它有一个 textLabel 和一个 busIcon 链接:

我正在使用viewFor annotation 委托方法为所有注释创建视图:

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

        

    

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

&

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

只是为了检查代码是否正常工作并在地图上显示示例视图,因为它们使用从 MKAnnotationView 继承的标准属性。

我不知道如何让viewFor annotation 方法使用我创建的XIB。有人可以帮我吗?我搜索了解决方案,但只在 Obj C 中找到了相关的内容。

谢谢!

【问题讨论】:

【参考方案1】:

1- 使用 xib 创建 UIView 的视图子类,例如 CallView

2- 内部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】:

基于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


    

为批注视图的 MarkerView 类创建 Xib

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
    

在您的 ViewController 中添加 MKMapView 委托

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
        
    




     

为地图视图添加注释

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)

    



【讨论】:

1) 注释视图的框架变为零,您必须手动设置它,这已经意味着一个错误; 2)注释在选择时开始略有跳跃。否决

以上是关于如何使用 XIB 制作自定义 MKAnnotationView的主要内容,如果未能解决你的问题,请参考以下文章

自定义 UITableViewCell 打开不同的 xib 子视图

我可以用故事板替换所有xib吗?

IOS谷歌地图自定义InfoWindow Xib错误

如何使用 xib 创建自定义视图

如何借助 AutoLayout 在自定义 UITableviewCell 中添加自定义 UIView(xib)?

如何使用 xib 文件自动布局宽度自定义 UITableViewCell