Reputation: 1451
I have a map view, which has two types of custom annotation views. I am wondering how to add different types of clusters for these views (depending on the types of annotation views). Currently I've tried to do everything as in the sample project from WWDC 2017's session 237 "What's new in MapKit". But when I register my cluster view, it does nothing (even not get called). I guess, that is because I use custom annotation views and don't register them, but instead, use MKMapViewDelegate
method mapView(_:viewFor:)
. Here is the code where I register my custom cluster annotation (ClusterView
is a subclass of MKAnnotationView
where I define my cluster annotation):
mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)
The code above is defined inside a viewDidLoad()
method, but again, it doesn't even get called. So, I think I should implement one of MKMapViewDelegate
methods: mapView(_:clusterAnnotationForMemeberAnnotations:)
. The problem is, I don't have any experience in adding cluster annotations, so I don't know how to implement it right. I've been looking for some examples on the Internet for several weeks but haven't found anything yet (only about third party libraries). If you know how to implement the above mentioned method, or other way of adding clusters to a mapView
that has different types of custom annotation views (without using third party libraries), I would appreciate your help.
Upvotes: 3
Views: 6229
Reputation: 351
Try to register your Annotations with String reuse Identifier:
mapView.register(AnnotationViewMarker.self, forAnnotationViewWithReuseIdentifier: "marker")
mapView.register(AppleClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: "cluster")
I already had this problem too. In case here is an example that work in my app:
override func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation || annotation is UserAnnotation {
return nil
}
if let marker = annotation as? WifiAnnotation {
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "marker") as? AnnotationViewMarker
if view == nil {
view = AnnotationViewMarker(annotation: marker, reuseIdentifier: "marker")
}
return view
} else if let cluster = annotation as? MKClusterAnnotation {
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "cluster") as? AppleClusterAnnotationView
if view == nil {
view = AppleClusterAnnotationView(annotation: cluster, reuseIdentifier: "cluster")
}
return view
}
return nil
}
here an exemple of my MKAnnotationView Class:
class AppleClusterAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
displayPriority = .defaultLow
//collisionMode = .circle
centerOffset = CGPoint(x: 0, y: -10) // Offset center point to animate better with marker annotations
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var annotation: MKAnnotation? {
willSet {
if let cluster = newValue as? MKClusterAnnotation {
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 40, height: 40))
let count = cluster.memberAnnotations.count
let uniCount = cluster.memberAnnotations.filter { member -> Bool in
return (member as! WifiAnnotation).wifiType != "Ad"
}.count
image = renderer.image { _ in
// Fill full circle with tricycle color
UIColor(red: 52/255, green: 131/255, blue: 223/255, alpha: 0.22).setFill()
UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 40, height: 40)).fill()
// Fill pie with unicycle color
UIColor(red: 52/255, green: 131/255, blue: 223/255, alpha: 0.22).setFill()
let piePath = UIBezierPath()
piePath.addArc(withCenter: CGPoint(x: 20, y: 20), radius: 20,
startAngle: 0, endAngle: (CGFloat.pi * 2.0 * CGFloat(uniCount)) / CGFloat(count),
clockwise: true)
piePath.addLine(to: CGPoint(x: 20, y: 20))
piePath.close()
piePath.fill()
// Fill inner circle with white color
UIColor.white.setFill()
UIBezierPath(ovalIn: CGRect(x: 8, y: 8, width: 24, height: 24)).fill()
// Finally draw count text vertically and horizontally centered
let attributes = [ NSAttributedStringKey.foregroundColor: UIColor.black,
NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 15)]
let text = "\(count)"
let size = text.size(withAttributes: attributes)
let rect = CGRect(x: 20 - size.width / 2, y: 20 - size.height / 2, width: size.width, height: size.height)
text.draw(in: rect, withAttributes: attributes)
}
}
}
}}
And finally my MKMarkerAnnotationView. With that you are all setup to make it works. You only have to setup your MKAnnotation.
MKMarkerAnnotationView:
class AnnotationViewMarker: MKMarkerAnnotationView {
override open var annotation: MKAnnotation? {
willSet {
if let annotation = newValue as? WifiAnnotation {
clusteringIdentifier = "WifiAnnotation"
if annotation.wifiType == "yyy" {
glyphImage = UIImage(named: "yyy")
selectedGlyphImage = UIImage(named: "yyy")
markerTintColor = UIColor("#303030")
glyphTintColor = .white
displayPriority = .defaultHigh
titleVisibility = .visible
animatesWhenAdded = true
} else {
glyphImage = UIImage(named: "xxx")
selectedGlyphImage = UIImage(named: "xxx")
markerTintColor = UIColor("#3483DF")
glyphTintColor = .white
displayPriority = .required
titleVisibility = .visible
animatesWhenAdded = true
let accessButton = UIButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
accessButton.setImage(self.accessAnnotation, for: .normal) //UIImage(named: "accessAnnotation"), for: .normal)
rightCalloutAccessoryView = accessButton
}
//collisionMode = .circle
}
}
}
}
Upvotes: 4