问题
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