Optimisation du code pour MKMapView – Grand nombre d'annotations

J'ai une vue modale dans mon application qui affiche un UIMapView. J'ajoute ensuite un grand nombre d'annotations (plus de 800) à cette vue cartographique (code ci-dessous).

Le problème est que l'user est obligé d'attendre une minute ou plus alors que toutes les broches se chargent. Aussi l'application devient lente une fois que tous les 800 épingles sont sur la carte.

Quelqu'un peut-il suggérer comment je peux améliorer mon code ci-dessous?

Je vous remercie.

#import "MapView.h" #import "MapPlaceObject.h" @implementation MapView @synthesize mapViewLink, mapLocations, detail, failedLoad; - (id)initWithNibName:(NSSsortingng *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } -(void)addPins { for (MapPlaceObject * info in mapLocations) { double latitude = info.longitude; double longitude = info.latitude; NSSsortingng * name = info.name; NSSsortingng * addressline = info.addressOne; NSSsortingng * postcode = info.postCode; NSSsortingng * addresscomma = [addressline ssortingngByAppendingSsortingng:@", "]; NSSsortingng * address = [addresscomma ssortingngByAppendingSsortingng:postcode]; CLLocationCoordinate2D coordinate; coordinate.latitude = latitude; coordinate.longitude = longitude; MyLocation *annotation = [[[MyLocation alloc] initWithName:name address:address coordinate:coordinate] autorelease]; [mapViewLink addAnnotation:annotation]; } } - (void)showLinks : (id)sender { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { detail = [[DetailViewController alloc] initWithNibName:@"DetailViewController-iPad" bundle:nil]; } else if (!detail) { NSLog(@"Detail is None"); detail = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil]; } int uniqueID = ((UIButton *)sender).tag; //PlaceObject *info = [mapLocations objectAtIndex:uniqueID]; detail.UniqueID = uniqueID; detail.hidesBottomBarWhenPushed = YES; [self.navigationController pushViewController:detail animated:YES]; self.detail = nil; [detail release]; } - (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{ if (annotation == mapView.userLocation){ return nil; //default to blue dot } MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"currentloc"]; annView.pinColor = MKPinAnnotationColorRed; nameSaved = annotation.title; for (PlaceObject * info in mapLocations) { if (info.name == nameSaved) { saveID = info.UniqueID; } } UIButton *advertButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; advertButton.frame = CGRectMake(0, 0, 23, 23); advertButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; advertButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; [advertButton addTarget:self action:@selector(showLinks:) forControlEvents:UIControlEventTouchUpInside]; advertButton.tag = saveID; annView.rightCalloutAccessoryView = advertButton; annView.animatesDrop=TRUE; annView.canShowCallout = YES; annView.calloutOffset = CGPointMake(-5, 5); return annView; } - (void)dealloc { [mapViewLink release]; [mapLocations release]; [detail release]; self.failedLoad = nil; [failedLoad release]; [super dealloc]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewWillAppear:(BOOL)animated { if (firstTime) { CLLocationCoordinate2D zoomLocation; zoomLocation.latitude = 51.50801; zoomLocation.longitude = -0.12789; MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 15*METERS_PER_MILE, 15*METERS_PER_MILE); MKCoordinateRegion adjustedRegion = [mapViewLink regionThatFits:viewRegion]; [mapViewLink setRegion:adjustedRegion animated:YES]; firstTime = NO; } } - (void)viewDidLoad { [super viewDidLoad]; firstTime = YES; failedLoad = [[NSMutableArray alloc]init]; self.mapLocations = [BluePlaqueDatabase database].mapInfo; [self addPins]; } - (void)viewDidUnload { [mapViewLink release]; mapViewLink = nil; [super viewDidUnload]; // Release any retained subviews of the main view. // eg self.myOutlet = nil; } 

Les deux plus grandes améliorations de vitesse que vous pouvez faire ici sont:

  • Implémenter la réutilisation de la vue d'annotation (en ce moment, elle crée une nouvelle vue chaque fois qu'elle doit afficher une annotation même si la même vue est à nouveau visible.
  • Changez la façon dont UniqueID est défini. Pour le définir, le code parcourt actuellement toutes les annotations chaque fois qu'il crée une vue d'annotation (ce qui peut se produire chaque fois que l'affichage de la carte est agrandi ou défilé – et pas seulement l'heure initiale).

Tout d'abord, au lieu de searchr l' UniqueID dans la méthode viewForAnnotation et d'utiliser une balise button pour transmettre l'identifiant d'annotation, ajoutez UniqueID tant que propriété à votre class d'annotations personnalisées MyLocation et définissez la propriété lorsque vous ajoutez l'annotation elle-même dans addPins :

 annotation.uniqueID = info.UniqueID; // <-- give id to annotation itself [mapViewLink addAnnotation:annotation]; 

Vous pouvez également append uniqueID tant que paramètre à la méthode initWithName au lieu d'affecter la propriété séparément.

Ensuite, pour implémenter la réutilisation de la vue d'annotation, la méthode viewForAnnotation doit ressembler à ceci:

 - (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{ if (annotation == mapView.userLocation){ return nil; //default to blue dot } NSSsortingng *reuseId = @"StandardPin"; MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId]; if (annView == nil) { annView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId] autorelease]; annView.pinColor = MKPinAnnotationColorRed; annView.animatesDrop = YES; annView.canShowCallout = YES; annView.calloutOffset = CGPointMake(-5, 5); UIButton *advertButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; advertButton.frame = CGRectMake(0, 0, 23, 23); advertButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter; advertButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter; annView.rightCalloutAccessoryView = advertButton; } else { //update the annotation property if view is being re-used... annView.annotation = annotation; } return annView; } 

Enfin, pour répondre au button, appuyez sur et UniqueID quel UniqueID pour afficher le détail, implémentez la méthode de délégué calloutAccessoryControlTapped :

 - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { MyLocation *myLoc = (MyLocation *)view.annotation; int uniqueID = myLoc.uniqueID; NSLog(@"calloutAccessoryControlTapped, uid = %d", uniqueID); //create, init, and show the detail view controller here... } 

Après tous ces changements, seul le chargement initial des annotations prendra la plupart du time. Si cela pose toujours un problème, une solution consiste à append uniquement des annotations visibles dans la région actuellement affichée et à append / supprimer des annotations lorsque l'user modifie la zone visible.

Je suis complètement d'accord avec Anna. Mais considérons que 800 AnnotationViews en même time se traduira par une interface laggy extrême. Donc, si votre carte doit fournir une interaction de l'user comme le défilement ou le zoom, mieux implémenter une sorte de clustering de vos vues d'annotations.