处理大量MKMapView标注

10

我有一个地图视图,其中包含大量的注释(3000+)。当用户缩放到合理的级别时,一切都很好且速度很快。

但是,当用户缩小视图并且大量注释进入视图时,由于一次性显示了如此多的注释,会出现很多减速。如何处理这种情况最好呢?

我当前正在使用的解决方案:

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

功能非常好,尽管由于循环的庞大大小,在缩放和滚动时会导致许多减速。我似乎想不出更好的处理方法,有没有一种只循环遍历当前显示的注释或类似方法来减小循环的大小?

3个回答

7

根据我理解你的代码,如果地图视图缩小到一定程度,你将隐藏所有注释视图。

但我认为以下代码更好:

- (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];
    }
}

针对以上问题,在大部分情况下,此代码所做的只是一些 if 条件判断。

另一个解决方案是在地图上不需要显示注释时将其移除。个人认为这样更好,因为此代码就无需为尚未在地图上显示的注释创建视图。


这样做可以加快速度 :) 我会结合donkim的建议一起实现,这样一切都应该飞快。 - Alex
你好,setMapViewsHidden中我该怎么做?是想要隐藏那些标注吗? - Misha
MapViewsHidden是一个BOOL属性。 - Daniel T.
removeAnnotations: 是一个非常昂贵的调用。我看到删除25个注释的数组需要60毫秒以上。单个注释的删除也很容易超过40毫秒。考虑到它必须在主线程上完成,这会破坏地图滚动的平滑性。如果有人关心的话,addAnnotations要便宜得多。 - Anton Tropashko

6
我建议你可以采取以下几点措施。第一,看一下 annotationsInMapRect: 方法。根据文档所说:
这个方法提供了一种快速检索地图中特定区域内标注对象的方式。这种方法要比自己在 annotations 属性中做线性搜索快得多。
第二,看一下 dequeueReusableAnnotationViewWithIdentifier: 方法。根据文档所说:
出于性能考虑,你通常应该在地图视图中重用 MKAnnotationView 对象。当注释视图离开屏幕时,地图视图会将它们移动到一个内部管理的重用队列中。当新的注释进入屏幕,并且你的代码被提示提供相应的注释视图时,你应该始终尝试在创建新视图之前出列现有视图。出列可在滚动等性能关键操作期间节省时间和内存。
最后,还有几点想法。不要每次调用 - (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated 方法都进行这些更改,如何根据其他规则(例如在定时器触发后 [确保每次调用时重置定时器])进行更改?另一个需要考虑的问题是:如何将非常接近彼此的注释组合在一起?假设你缩小到罗德岛看起来非常小,可能只有十几个像素宽,而在罗德岛有100个点 - 你应该只显示一个图钉。
希望这可以帮到你!

嘿,donkim,我已经使用了dequeueReusableAnnotationViewWithIdentifier:,但我会研究你建议的其他技术,以尝试让所有东西运行得更快。很遗憾annotationsInMapRect仅在> 4.2中受支持,似乎它可以解决我所有的麻烦。 - Alex
根据我的经验,dequeueReusableAnnotationViewWithIdentifier 从不返回旧的注释。 - malhal
annotationsInMapRect确实比自己计算坐标要快得多。 - Skoua

3
我最终使用OCMapView将我的注释分组。它是免费的,并且非常容易实现到现有代码中。
当你缩小地图时,它会自动将注释分组,而当你放大地图时,它们会像应该那样单独显示,不再分组。
我知道这是一个老问题,但对于任何试图做同样事情的人来说,这是一个很好的选择。

感谢您重新访问这个老问题。指出有用的库来帮助解决问题总是很好的,即使已经有了答案。 - Daniel T.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接