Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mkmap iOS11 clusters doesn't split up after max zoom, how to set it up?

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: enter image description here

Here if I zoom at the maximum: Well, one of the cluster views split into two, but doesn't reveal the 4 annotations.
enter image description here

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 ?

like image 220
Cory Avatar asked Oct 19 '17 10:10

Cory


2 Answers

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
}
like image 196
Ric Santos Avatar answered Nov 19 '22 04:11

Ric Santos


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

like image 20
Den Jo Avatar answered Nov 19 '22 06:11

Den Jo