根据缩放级别分组/取消组合MKAnnotation(并保持快速)

我有一个有很多注释的mapview,其中大多数都非常接近。 我想要做的是类似于iOS上的Photo应用程序,当它们彼此太靠近时,注释被分组,每当你缩小时,如果它们相距太远,它们就会被分组。

我已经看过这个问题,但给出的答案并不是我想要的。

我正在寻找可以自己实现的库或算法。

看看这里是为了看到完整的答案。 它包含MapKit和Google Maps的两种实现。 该代码的灵感来自WWDC 2011video,并且在我的应用程序中运行良好。

我在这里发布了MapKit的代码,但在我的其他答案中有一些有用的评论。

- (void)didZoom:(UIGestureRecognizer*)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded){ [self updateVisibleAnnotations]; } } - (void)updateVisibleAnnotations { static float marginFactor = 2.0f; static float bucketSize = 50.0f; MKMapRect visibleMapRect = [self.mapView visibleMapRect]; MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height); CLLocationCoordinate2D leftCoordinate = [self.mapView convertPoint:CGPointZero toCoordinateFromView:self.view]; CLLocationCoordinate2D rightCoordinate = [self.mapView convertPoint:CGPointMake(bucketSize, 0) toCoordinateFromView:self.view]; double gridSize = MKMapPointForCoordinate(rightCoordinate).x - MKMapPointForCoordinate(leftCoordinate).x; MKMapRect gridMapRect = MKMapRectMake(0, 0, gridSize, gridSize); double startX = floor(MKMapRectGetMinX(adjustedVisibleMapRect) / gridSize) * gridSize; double startY = floor(MKMapRectGetMinY(adjustedVisibleMapRect) / gridSize) * gridSize; double endX = floor(MKMapRectGetMaxX(adjustedVisibleMapRect) / gridSize) * gridSize; double endY = floor(MKMapRectGetMaxY(adjustedVisibleMapRect) / gridSize) * gridSize; gridMapRect.origin.y = startY; while(MKMapRectGetMinY(gridMapRect) <= endY) { gridMapRect.origin.x = startX; while (MKMapRectGetMinX(gridMapRect) <= endX) { NSSet *allAnnotationsInBucket = [self.allAnnotationMapView annotationsInMapRect:gridMapRect]; NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect]; NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) { BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]]; BOOL shouldBeMerged = NO; if (isPointMapItem) { PointMapItem *pointItem = (PointMapItem *)obj; shouldBeMerged = pointItem.shouldBeMerged; } return shouldBeMerged; }] mutableCopy]; NSSet *notMergedAnnotationsInBucket = [allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) { BOOL isPointMapItem = [obj isKindOfClass:[PointMapItem class]]; BOOL shouldBeMerged = NO; if (isPointMapItem) { PointMapItem *pointItem = (PointMapItem *)obj; shouldBeMerged = pointItem.shouldBeMerged; } return isPointMapItem && !shouldBeMerged; }]; for (PointMapItem *item in notMergedAnnotationsInBucket) { [self.mapView addAnnotation:item]; } if(filteredAnnotationsInBucket.count > 0) { PointMapItem *annotationForGrid = (PointMapItem *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket]; [filteredAnnotationsInBucket removeObject:annotationForGrid]; annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects]; [self.mapView addAnnotation:annotationForGrid]; //force reload of the image because it's not done if annotationForGrid is already present in the bucket!! MKAnnotationView* annotationView = [self.mapView viewForAnnotation:annotationForGrid]; NSString *imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO]; UILabel *countLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 2, 8, 8)]; [countLabel setFont:[UIFont fontWithName:POINT_FONT_NAME size:10]]; [countLabel setTextColor:[UIColor whiteColor]]; [annotationView addSubview:countLabel]; imageName = [AnnotationsViewUtils imageNameForItem:annotationForGrid selected:NO]; annotationView.image = [UIImage imageNamed:imageName]; if (filteredAnnotationsInBucket.count > 0){ [self.mapView deselectAnnotation:annotationForGrid animated:NO]; } for (PointMapItem *annotation in filteredAnnotationsInBucket) { [self.mapView deselectAnnotation:annotation animated:NO]; annotation.clusterAnnotation = annotationForGrid; annotation.containedAnnotations = nil; if ([visibleAnnotationsInBucket containsObject:annotation]) { CLLocationCoordinate2D actualCoordinate = annotation.coordinate; [UIView animateWithDuration:0.3 animations:^{ annotation.coordinate = annotation.clusterAnnotation.coordinate; } completion:^(BOOL finished) { annotation.coordinate = actualCoordinate; [self.mapView removeAnnotation:annotation]; }]; } } } gridMapRect.origin.x += gridSize; } gridMapRect.origin.y += gridSize; } } - (id)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations { NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect]; NSSet *annotationsForGridSet = [annotations objectsPassingTest:^BOOL(id obj, BOOL *stop) { BOOL returnValue = ([visibleAnnotationsInBucket containsObject:obj]); if (returnValue) { *stop = YES; } return returnValue; }]; if (annotationsForGridSet.count != 0) { return [annotationsForGridSet anyObject]; } MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMinX(gridMapRect), MKMapRectGetMidY(gridMapRect)); NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2) { MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id)obj1).coordinate); MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id)obj2).coordinate); CLLocationDistance distance1 = MKMetersBetweenMapPoints(mapPoint1, centerMapPoint); CLLocationDistance distance2 = MKMetersBetweenMapPoints(mapPoint2, centerMapPoint); if (distance1 < distance2) { return NSOrderedAscending; } else if (distance1 > distance2) { return NSOrderedDescending; } return NSOrderedSame; }]; return [sortedAnnotations objectAtIndex:0]; } 

Apple的WWDC 2011会议video之一展示了如何做到这一点。 转到https://developer.apple.com/videos/wwdc/2011/ (必须是注册开发人员)并滚动到标题为“使用MapKit按地理位置可视化信息”的video。 基本思路是使用屏幕外地图视图来保存所有注释,并根据需要将它们复制到屏幕上的地图视图,确保您不会尝试一次显示太多。 它甚至可以在缩放时使用注释进行漂亮的动画制作。