Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling large quantity of MKMapView Annotations

I have a map view with a large quantity of annotations (3000+), when the user is zoomed to a reasonable level all is well and fast.

Although when the user zooms out and a large quanitiy of annotations come into view there is a lot of slow down due to the sheer amount of annotations being shown at once. What is the best way to handle this?

The current solution i am using:

- (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated {

    NSArray *annotations = [_mapView annotations];  
    MapAnnotation *annotation = nil; 

    for (int i=0; i<[annotations count]; i++)
    {
        annotation = (MapAnnotation*)[annotations objectAtIndex:i];
        if (_mapView.region.span.latitudeDelta > .010)
        {
            [[_mapView viewForAnnotation:annotation] setHidden:YES];
            warningLabel.hidden = NO;
        }
        else {
            [[_mapView viewForAnnotation:annotation] setHidden:NO];
            warningLabel.hidden = YES;
        }
    }
}

Works great although due to the sheer size of the loop this causes a lot of slow down when zooming in and out and scrolling around. I can't seem to think of a better way to handle this, is there a way to only loop through the annotations currently being displayed or something along those lines to reduce the size of the loop?

like image 272
Alex Avatar asked Jan 23 '11 20:01

Alex


3 Answers

As I understand your code, you are hiding all the annotations' views if the mapView is zoomed out more than a certain specified value.

It seems to me that something more like the following would be better:

- (void)mapView: (MKMapView*)_mapView regionDidChangeAnimated: (BOOL)animated
{
    if (_mapView.region.span.latitudeDelta > .010 && self.mapViewsHidden == NO) {
        for (MapAnnotation* annotation in _mapView.annotations) {
            [[_mapView viewForAnnotation: annotation] setHidden: YES];
        }
        [self.warningLabel setHidden: NO];
        [self setMapViewsHidden: YES];
    }
    else if (_mapView.region.span.latitudeDelta <= .010 && self.mapViewsHidden == YES) {
        for (MapAnnotation* annotation in _mapView.annotations) {
            [[_mapView viewForAnnotation: annotation] setHidden: NO];
        }
        [self.warningLabel setHidden: YES];
        [self setMapViewsHidden: NO];
    }
}

With the above, in most cases the only thing this code does is a couple of if checks.

Another solution would be to remove the annotations when they shouldn't show on the map. Personally, I think this would be better, that way the code doesn't have to create views for annotations that haven't been shown on the map yet.

like image 113
Daniel T. Avatar answered Nov 12 '22 01:11

Daniel T.


I would suggest a couple of things. One, look at the method annotationsInMapRect:. According to the docs, it says:

This method offers a fast way to retrieve the annotation objects in a particular portion of the map. This method is much faster than doing a linear search of the objects in the annotations property yourself.

Two, look at dequeueReusableAnnotationViewWithIdentifier:. According to the docs again, it says:

For performance reasons, you should generally reuse MKAnnotationView objects in your map views. As annotation views move offscreen, the map view moves them to an internally managed reuse queue. As new annotations move onscreen, and your code is prompted to provide a corresponding annotation view, you should always attempt to dequeue an existing view before creating a new one. Dequeueing saves time and memory during performance critical operations such as scrolling.

Finally, a couple of thoughts. Instead of doing these changes every time the - (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated method is called, how about doing it according to some other rule (such as after a timer gets fired [make sure to reset the timer every time this gets called])? Another thing to consider: how about grouping annotations together that are super close to each other? Let's say you're zoomed out to where Rhode Island looks super small, maybe just a dozen pixels wide, and you have 100 points in Rhode Island -- you should just display one pin.

Hope this helps!

like image 38
donkim Avatar answered Nov 12 '22 00:11

donkim


I ended up grouping my annotations in clusters using OCMapView. Its free, and very easy to implement into existing code.

It automatically groups your annotations when zoomed out and as you zoom in they appear unclustered as they should.

I know this is an old question but its a great option for anyone else trying to do the same thing.

like image 3
RyanG Avatar answered Nov 11 '22 23:11

RyanG