如何使用 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 子视图