I am trying to build a MKMapView to display the photos just as the Photos app in iOS as a practice. However, I see the following performance issue when building the application.
When I am loading the annotations to the mapView, it looks like the
dequeueReusableAnnotationView()
never works, meaning it always instantiates a new MapAnnotationView instead of reusing an existing view (I validated to add a log message to the init() function of theMapAnnotationView
and the count of the log message equals to the number of annotations I want to add. If I do a zoom in/out, it looks fine though, and is calling the prepareForReuse() function, and it does not create any newMapAnnotationView
. As we can foresee, this is bringing a huge impact to the memory in this case.I am using the default clustering feature from MKMapView, and the problem I notice is that the cluster is only working after all the annotations are added. This does not remove any
MKAnnotationView
added by #1 as I inspected the memory, even these individualMKAnnotationView
are not shown at all.The clustering function is also added with the clustering ID. And this should not be the main issue here since the clustering function is working fine but the performance is bad. Also one question I do have is -- why this cluster function
mapView(_ mapView: MKMapView, clusterAnnotationForMemberAnnotations memberAnnotations: [MKAnnotation])
is not called beforemapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation)
in MKMapView? Shouldn't this help performance and avoid initiating unnecessary views that are not going to be displayed?
before Below are the code snippets for showing the skeleton.
// 1. initialization and after wards, registers the reuse identifier
mapView.register(ClusterAnnotationView.self, forAnnotationViewWithReuseIdentifier: mapViewClusterAnnotationIdentifier)
mapView.register(IndividualMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: mapViewAnnotationIdentifier)
// 2. registers the assets to the map with annotation.
allAssets = PHAsset.fetchAssets(with: fetchOptions)
for index in 0...allAssets.count-1 {
autoreleasepool {
let asset = allAssets[index]
guard let coordinate = asset.location?.coordinate else { return }
let mapAnnotation = IndividualMapAnnotation(index: index, coordinate: coordinate)
mapAnnotations.append(mapAnnotation)
}
}
//3. generates the view for the annotation.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard mapView == self.mapView else { return nil }
if annotation is ClusterMapAnnotation {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: mapViewClusterAnnotationIdentifier) as? ClusterAnnotationView
annotationView?.displayPriority = .required
annotationView?.canShowCallout = false
return annotationView
} else if annotation is IndividualMapAnnotation {
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: mapViewAnnotationIdentifier) as? IndividualMapAnnotationView
annotationView?.delegate = self
annotationView?.canShowCallout = false
annotationView?.displayPriority = .defaultLow
annotationView?.clusteringIdentifier = clusteringUuid
return annotationView
}
return nil
}
//
func mapView(_ mapView: MKMapView, clusterAnnotationForMemberAnnotations memberAnnotations: [MKAnnotation]) -> MKClusterAnnotation { ... }
Again, with step1 and step3, i would expect that all the views will be reused but it does not look like the case, and the memory usage is also not idea even after cluster (and the IndividualMapAnnotationView is not even displayed!). Can anyone help me to check if I miseed anything? Or should I do any manual filtering to control whether to create the IndividualMapAnnotationView
?