iPhone Mkmapview注释集群
-
22-10-2019 - |
题
我有很多图钉可以放在我的地图上,所以我认为聚集这些注释是一个好主意。我不太确定如何在iPhone上实现这一目标,我能够使用Google Maps和一些JavaScript示例来解决一些问题。但是iPhone使用其MKMAPVIEW,我不知道如何在其中聚集注释。
您知道和良好的想法或框架吗?谢谢。
解决方案
由于这是一个非常普遍的问题,我需要一个解决方案,所以我写了一个自定义的MKMAPVIEW子类,该子类支持聚类。然后我将其提供给开源!你可以在这里得到它: https://github.com/yinkou/ocmapview.
它管理注释的聚类,您可以自己处理他们的观点。您不必做任何事情,而要复制 OCMapView
您项目的文件夹,创建一个 MKMapView
在您的笔尖中,将其班级设置为 OCMapView
. 。 (或创建并将其像常规的代码委派 MKMapView
)
其他提示
您不一定需要使用第三方框架 - (NSSet *)annotationsInMapRect:(MKMapRect)mapRect
您可以用来进行聚类。
查看WWDC11会话视频'用mapkit在地理上可视化信息'。大约一半的时间解释了如何做。但是我将为您总结一下概念:
- 使用两个地图(第二个地图永远不会添加到视图层次结构)
- 第二地图包含所有注释(再一次,它从未被绘制)
- 将地图区域分为正方形
- 利用
-annotationsInMapRect
从看不见地图获取注释数据的方法 - 可见的地图从看不见的地图中从这些数据中构建其注释
幸运的是,您不再需要第三方框架了。 iOS 11具有本机聚类支持。
您需要实施 mapView:clusterAnnotationForMemberAnnotations:
方法。
在Apple示例中获取更多详细信息: https://developer.apple.com/sample-code/wwdc/2017/mapkit-sample.zip
通过使用Apple演示代码,可以在我们的代码中易于实现聚类概念。 参考链接
简而言之,我们可以使用以下代码进行群集
实现聚类的步骤
步骤1 : 重要的是,对于聚类,我们使用两个mapView(allannotationsmapview),一个是参考(allannotationsmapview)。
@property (nonatomic, strong) MKMapView *allAnnotationsMapView;
@property (nonatomic, strong) IBOutlet MKMapView *mapView;
在 ViewDidload
_allAnnotationsMapView = [[MKMapView alloc] initWithFrame:CGRectZero];
第2步 : 将所有注释添加到_allannotationsmapview中,在_photos中是注释数组。
[_allAnnotationsMapView addAnnotations:_photos];
[self updateVisibleAnnotations];
步骤3: 在此光通道中添加下面的聚类方法是自定义注释。MapViewDelegate方法
- (void)mapView:(MKMapView *)aMapView regionDidChangeAnimated:(BOOL)animated {
[self updateVisibleAnnotations];
}
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views {
for (MKAnnotationView *annotationView in views) {
if (![annotationView.annotation isKindOfClass:[PhotoAnnotation class]]) {
continue;
}
PhotoAnnotation *annotation = (PhotoAnnotation *)annotationView.annotation;
if (annotation.clusterAnnotation != nil) {
// animate the annotation from it's old container's coordinate, to its actual coordinate
CLLocationCoordinate2D actualCoordinate = annotation.coordinate;
CLLocationCoordinate2D containerCoordinate = annotation.clusterAnnotation.coordinate;
// since it's displayed on the map, it is no longer contained by another annotation,
// (We couldn't reset this in -updateVisibleAnnotations because we needed the reference to it here
// to get the containerCoordinate)
annotation.clusterAnnotation = nil;
annotation.coordinate = containerCoordinate;
[UIView animateWithDuration:0.3 animations:^{
annotation.coordinate = actualCoordinate;
}];
}
}
}
聚类处理方法
- (id<MKAnnotation>)annotationInGrid:(MKMapRect)gridMapRect usingAnnotations:(NSSet *)annotations {
// first, see if one of the annotations we were already showing is in this mapRect
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];
}
// otherwise, sort the annotations based on their distance from the center of the grid square,
// then choose the one closest to the center to show
MKMapPoint centerMapPoint = MKMapPointMake(MKMapRectGetMidX(gridMapRect), MKMapRectGetMidY(gridMapRect));
NSArray *sortedAnnotations = [[annotations allObjects] sortedArrayUsingComparator:^(id obj1, id obj2) {
MKMapPoint mapPoint1 = MKMapPointForCoordinate(((id<MKAnnotation>)obj1).coordinate);
MKMapPoint mapPoint2 = MKMapPointForCoordinate(((id<MKAnnotation>)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;
}];
PhotoAnnotation *photoAnn = sortedAnnotations[0];
NSLog(@"lat long %f %f", photoAnn.coordinate.latitude, photoAnn.coordinate.longitude);
return sortedAnnotations[0];
}
- (void)updateVisibleAnnotations {
// This value to controls the number of off screen annotations are displayed.
// A bigger number means more annotations, less chance of seeing annotation views pop in but decreased performance.
// A smaller number means fewer annotations, more chance of seeing annotation views pop in but better performance.
static float marginFactor = 2.0;
// Adjust this roughly based on the dimensions of your annotations views.
// Bigger numbers more aggressively coalesce annotations (fewer annotations displayed but better performance).
// Numbers too small result in overlapping annotations views and too many annotations on screen.
static float bucketSize = 60.0;
// find all the annotations in the visible area + a wide margin to avoid popping annotation views in and out while panning the map.
MKMapRect visibleMapRect = [self.mapView visibleMapRect];
MKMapRect adjustedVisibleMapRect = MKMapRectInset(visibleMapRect, -marginFactor * visibleMapRect.size.width, -marginFactor * visibleMapRect.size.height);
// determine how wide each bucket will be, as a MKMapRect square
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);
// condense annotations, with a padding of two squares, around the visibleMapRect
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;
// for each square in our grid, pick one annotation to show
gridMapRect.origin.y = startY;
while (MKMapRectGetMinY(gridMapRect) <= endY) {
gridMapRect.origin.x = startX;
while (MKMapRectGetMinX(gridMapRect) <= endX) {
NSSet *allAnnotationsInBucket = [self.allAnnotationsMapView annotationsInMapRect:gridMapRect];
NSSet *visibleAnnotationsInBucket = [self.mapView annotationsInMapRect:gridMapRect];
// we only care about PhotoAnnotations
NSMutableSet *filteredAnnotationsInBucket = [[allAnnotationsInBucket objectsPassingTest:^BOOL(id obj, BOOL *stop) {
return ([obj isKindOfClass:[PhotoAnnotation class]]);
}] mutableCopy];
if (filteredAnnotationsInBucket.count > 0) {
PhotoAnnotation *annotationForGrid = (PhotoAnnotation *)[self annotationInGrid:gridMapRect usingAnnotations:filteredAnnotationsInBucket];
[filteredAnnotationsInBucket removeObject:annotationForGrid];
// give the annotationForGrid a reference to all the annotations it will represent
annotationForGrid.containedAnnotations = [filteredAnnotationsInBucket allObjects];
[self.mapView addAnnotation:annotationForGrid];
for (PhotoAnnotation *annotation in filteredAnnotationsInBucket) {
// give all the other annotations a reference to the one which is representing them
annotation.clusterAnnotation = annotationForGrid;
annotation.containedAnnotations = nil;
// remove annotations which we've decided to cluster
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;
}
}
通过遵循上述步骤,我们可以在MapView上实现聚类,因此不必使用任何第三方代码或框架。请检查 Apple样本代码 这里。如果您对此有任何疑问,请告诉我。
您是否看过AdClusterMapView? https://github.com/applidium/adclustermapview
正是这样。
我只是想聚集针,只是显示了它的数字。以下一个https://www.cocoacontrols.com/controls/qtree-objc 适合我的期望。
最近,我分叉了另一个答案中提到的AdClusterMapview,并解决了与该项目相关的问题的许多(如果不是全部)。这是一种KD-Tree算法,可以使聚类动画。
尝试此框架(xmapview.framework);现在它支持iOS 8。
该框架不需要您更改当前的项目结构,并且可以直接用于MKMAPVIEW。有一个zip文件。它为您提供了一个示例,可以一次群集200引脚。在iPod中测试它后,我发现它非常平滑。
http://www.xuliu.info/xmapview.html
该库支持:
- 聚类不同的类别
- 聚类所有类别
- 设置自己的群集半径等等
- 隐藏或显示某个类别
- 单独处理并控制地图中的每个销钉
这里有一个非常酷且维护良好的图书馆,既可objective-c和swift: https://github.com/bigfish24/abfrealmmapview
它的聚集确实很好,并且由于与 领域.