Optimalisatiecode voor MKMapView - groot aantal annotaties

Ik heb een modale weergave in mijn app waarin een UIMapView wordt weergegeven. Vervolgens voeg ik een groot aantal annotaties (meer dan 800) toe aan deze kaartweergave (onderstaande code).

Het probleem is dat de gebruiker een minuut of zo moet wachten terwijl alle pinnen worden geladen. Ook wordt de app traag zodra alle 800 pinnen op de kaart staan.

Kan iemand voorstellen hoe ik mijn code hieronder kan verbeteren?

Dank je.

#import "MapView.h"
#import "MapPlaceObject.h"


@implementation MapView
@synthesize mapViewLink, mapLocations, detail, failedLoad;

- (id)initWithNibName:(NSString *)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;

    NSString * name = info.name;
    NSString * addressline = info.addressOne;
    NSString * postcode = info.postCode;

    NSString * addresscomma = [addressline stringByAppendingString:@", "];
    NSString * address = [addresscomma stringByAppendingString: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 ) 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.
// e.g. self.myOutlet = nil;
}
8

2 antwoord

De twee grootste snelheidsverbeteringen die je hier kunt maken zijn:

  • Hergebruik van annotatieweergave implementeren (op dit moment wordt een nieuwe weergave gemaakt telkens wanneer een annotatie moet worden weergegeven, ook als dezelfde weer wordt weergegeven.
  • Wijzig hoe de UniqueID is ingesteld. Om dit in te stellen, doorloopt de code momenteel alle annotaties telkens wanneer een annotatieweergave wordt gemaakt (wat kan gebeuren wanneer de kaartweergave is ingezoomd of gescrolld - niet alleen de begintijd).

Ten eerste, in plaats van te zoeken naar de UniqueID in de viewForAnnotation -methode en een knoptag te gebruiken om de annotatie-id door te geven, voeg UniqueID toe als een eigenschap van uw aangepaste annotatieklasse MijnLocatie en stel de eigenschap in wanneer u de annotatie zelf toevoegt in addPins :

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

U kunt ook uniqueID toevoegen als parameter voor de initWithName -methode in plaats van de eigenschap afzonderlijk toe te wijzen.


Next, to implement annotation view re-use, the viewForAnnotation method should look like this:

- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id ) annotation{

    if (annotation == mapView.userLocation){
        return nil; //default to blue dot
    }    

    NSString *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;
}


Finally, to respond to the button press and figure out which UniqueID to show the detail for, implement the calloutAccessoryControlTapped delegate method:

- (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...
}


After all these changes, only the initial loading of the annotations will take up most of the time. If that is still a problem, one solution is to only add annotations that would be visible in the currently displayed region and add/remove annotations as the user changes the visible region.

10
toegevoegd

Ik ben het helemaal met Anna eens. Maar bedenk dat 800 AnnotationViews tegelijkertijd resulteren in een extreme laggy-interface. Dus als je kaart gebruikersinteractie moet bieden zoals scrollen of zoomen, kun je beter een soort clustering van je annotatieweergaven implementeren.

4
toegevoegd