ribl / FBAnnotationClusteringSwift

Swift translation of FB Annotation Clustering, which clusters pins on the map for iOS.
http://ribl.co/blog/2015/05/28/map-clustering-with-swift-how-we-implemented-it-into-the-ribl-ios-app/
MIT License
311 stars 109 forks source link

Reuse current cluster #12

Open Quentin-Malgaud opened 8 years ago

Quentin-Malgaud commented 8 years ago

First thank you @chenr2 for your work. I wouldn't have been able to translate this wonderful library by myself. Unfortunately I am facing an issue that is somewhat common in the objective-c version: reusing the current cluster. A solution has been posted here https://github.com/infinum/FBAnnotationClustering/issues/2 but I am not able to translate it in swift. Does anyone know how to do it?

Thank you very much.

chenr2 commented 8 years ago

Thanks for raising this issue @Quentin-Malgaud. I just pushed up a new branch -- can you see if this fixes the issue?: https://github.com/ribl/FBAnnotationClusteringSwift/tree/reuseCluster

Problem: It looks like clusters are always being re-drawn. Like, if I see a cluster of "16" and pan the map slightly, the code removes and re-adds the "16" cluster, instead of just keeping it on the screen.

To test this, I added the following map view delegate method to FBViewController.swift:

func mapView(mapView: MKMapView, didAddAnnotationViews views: [MKAnnotationView]) {
    print("views: \(views)")
}

I end up seeing a ton of output in the console when panning the map slightly, even if the clusters shouldn't have changed.

Approach: While the Swift code I translated in displayAnnotations(_:onMapView:) compiles, it doesn't appear to work properly (my fault). The code that determines whether to reuse or "keep" a cluster on the screen relies on NSMutableSet.intersectSet. For some reason, I can't get intersectSet to work even in a Playground even with simple Int arrays.

I ended up re-doing displayAnnotations(_:onMapView:) so that it uses Swift's Set rather than NSMutableSet. Since the MKAnnotation protocol does not conform to the Hashable protocol, I had to first cast the annotations to FBAnnotationCluster before I could perform Set manipulations.

Within FBAnnotationCluster.swift, I implemented/overrided methods/vars for isEqual, ==, and hashValue. Two clusters are considered equal if they have the same hash and annotation count. And the hash is determined by deriving a semi-unique Int based on the longitude and latitude. This code is based on the work done in the original repo that you linked to, but with some adjustments.

Now, when I pan the map slightly, I don't see any console output coming from didAddAnnotationViews (for reused clusters, that is). I broke individual map pins, but fixed that in a second commit. I'll need your help to see if everything else is working (for ex. I didn't test the user location annotation).

On a side note, I did come across another Swift map clustering library from Realm. I haven't tried it myself, but it looks promising!