First, my code is perfectly running. I have well set up and mapView.register my annotation markers and cluster.
When I zoom out the annotations fusion as expected in my cluster views, when I zoom in, same good result, except at a certain point. When too many annotations are too close from each other, the cluster view doesn't split up into my two annotation views anymore.
So I search a way to be able to setup this "zoom level" that will makes appear my two annotations even if there are really close from each other.
Here are my cluster views with a high zoom on the map:
Here if I zoom at the maximum:
Well, one of the cluster views split into two, but doesn't reveal the 4 annotations.
I also try to setup the displayPriority to be higher for my two annotations, than the cluster view, but the result is still the same. Any ideas ?
You will need to keep track of the zoom level of the map, and reload your annotations when you cross a zoom level that you specify.
private let maxZoomLevel = 9
private var previousZoomLevel: Int?
private var currentZoomLevel: Int? {
willSet {
self.previousZoomLevel = self.currentZoomLevel
}
didSet {
// if we have crossed the max zoom level, request a refresh
// so that all annotations are redrawn with clustering enabled/disabled
guard let currentZoomLevel = self.currentZoomLevel else { return }
guard let previousZoomLevel = self.previousZoomLevel else { return }
var refreshRequired = false
if currentZoomLevel > self.maxZoomLevel && previousZoomLevel <= self.maxZoomLevel {
refreshRequired = true
}
if currentZoomLevel <= self.maxZoomLevel && previousZoomLevel > self.maxZoomLevel {
refreshRequired = true
}
if refreshRequired {
// remove the annotations and re-add them, eg
let annotations = self.mapView.annotations
self.mapView.removeAnnotations(annotations)
self.mapView.addAnnotations(annotations)
}
}
}
private var shouldCluster: Bool {
if let zoomLevel = self.currentZoomLevel, zoomLevel <= maxZoomLevel {
return false
}
return true
}
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// https://stackoverflow.com/a/40616239/883413
let zoomWidth = mapView.visibleMapRect.size.width
let zoomLevel = Int(log2(zoomWidth))
self.currentZoomLevel = zoomLevel
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// for me, annotation reuse doesn't work with clustering
let annotationView = CustomAnnotationView(annotation: annotation)
if self.shouldCluster {
annotationView.clusteringIdentifier = "custom-id"
} else {
annotationView.clusteringIdentifier = nil
}
return annotationView
}
In my case, ! EVERY TIME ! I didn't update the clusteringIdentifier
in "func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation)"
When the MKAnnotationView is reused by the mapView.dequeueReusableAnnotationView(withIdentifier: "identifier", for: annotation), the clusteringIdentifier will be nil. (reset)
That's the reason why the clusters doesn't work.
AnnotationView.swift
import MapKit
// MARK: - Define
struct AnnotationViewInfo {
static let identifier = "AnnotationView"
}
final class AnnotationView: MKAnnotationView {
// MARK: - Initializer
override init(annotation: MKAnnotation!, reuseIdentifier: String!) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
setView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setView()
}
// MARK: - Value
// MARK: Public
override var annotation: MKAnnotation? {
willSet { update(annotation: newValue) }
}
// MARK: - Function
// MARK: Private
private func setView() {
if #available(iOS 11.0, *) {
collisionMode = .rectangle
clusteringIdentifier = AnnotationViewInfo.identifier
}
canShowCallout = true
image = #imageLiteral(resourceName: "pin01").resizedImage(size: CGSize(width: #imageLiteral(resourceName: "pin01").size.width/4.0, height: #imageLiteral(resourceName: "pin01").size.height/4.0), scale: 1.0)
}
private func update(annotation: MKAnnotation?) {
if #available(iOS 11.0, *) {
clusteringIdentifier = AnnotationViewInfo.identifier
}
// TODO: Update the annotationView
}
}
MKMapViewDelegate
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if #available(iOS 11.0, *) {
switch annotation {
case is PointAnnotation: return mapView.dequeueReusableAnnotationView(withIdentifier: AnnotationView1Info.identifier, for: annotation)
case is MKClusterAnnotation: return mapView.dequeueReusableAnnotationView(withIdentifier: ClusterAnnotationViewInfo.identifier, for: annotation)
case is MKUserLocation: return nil
default: return nil
}
} else {
return nil
}
}
Key Point (You must update the "clusteringIdentifier" every time.)
private func update(annotation: MKAnnotation?) {
if #available(iOS 11.0, *) {
clusteringIdentifier = AnnotationViewInfo.identifier
}
// TODO: Update the annotationView
}
}
Sample Project Here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With