在地图视图的可见矩形中显示MKMapViewAnnotations

我在path式视差表视图标题中显示MKMapView。 要创build效果,mapView边界大于用户可见的区域。 我需要设置地图视图区域,使得所有地图的注解都包含在MKMapView的可视区域中。 什么是最好的方法来做到这一点?

MKMapView有限的可见区域

编辑清楚:这是一个用例。 mapView大小为320 x 380.然而,可见区域由rect(0.0,20.0,320.0,100.0)定义。 我需要设置区域,使得所有的注解都出现在mapView中的这个矩形中。

设置地图区域,使所有的注解包含在MKMapView的某个部分可以分三步完成。 input是mapViewannotationsFrame

  1. 计算包含所有注释的MKMapRect mapRect
  2. mapView.boundsannotationsFrame计算填充插入。
  3. 在地图视图上调用-setVisibleMapRect:edgePadding:animated:

以下是一个testing的屏幕截图。 红色覆盖图显示了annotationsFrame

屏幕显示所有注释位于给定注释框内的测试结果

这是代码。 注意 :所有的方法都是简单的把它添加到你的代码中,而不是用边缘情况来testing,比如用相同的坐标传递n个注释,或者注释太远以至于地图也不得不被缩小很多,或者坐标以+/- 180度的经度跨越地图的边缘。

 - (void)zoomAnnotationsOnMapView:(MKMapView *)mapView toFrame:(CGRect)annotationsFrame animated:(BOOL)animated { if (_mapView.annotations.count < 2) return; // Step 1: make an MKMapRect that contains all the annotations NSArray *annotations = _mapView.annotations; id <MKAnnotation> firstAnnotation = [annotations objectAtIndex:0]; MKMapPoint minPoint = MKMapPointForCoordinate(firstAnnotation.coordinate); MKMapPoint maxPoint = minPoint; for (id <MKAnnotation> annotation in annotations) { MKMapPoint point = MKMapPointForCoordinate(annotation.coordinate); if (point.x < minPoint.x) minPoint.x = point.x; if (point.y < minPoint.y) minPoint.y = point.y; if (point.x > maxPoint.x) maxPoint.x = point.x; if (point.y > maxPoint.y) maxPoint.y = point.y; } MKMapRect mapRect = MKMapRectMake(minPoint.x, minPoint.y, maxPoint.x - minPoint.x, maxPoint.y - minPoint.y); // Step 2: Calculate the edge padding UIEdgeInsets edgePadding = UIEdgeInsetsMake( CGRectGetMinY(annotationsFrame), CGRectGetMinX(annotationsFrame), CGRectGetMaxY(mapBounds) - CGRectGetMaxY(annotationsFrame), CGRectGetMaxX(mapBounds) - CGRectGetMaxX(annotationsFrame) ); // Step 3: Set the map rect [mapView setVisibleMapRect:mapRect edgePadding:edgePadding animated:animated]; } 

如果你去完美的位置(谁不是),这里有三件事情要考虑:

  1. 代码确保所有的坐标都在annotationsFrame ,但注释本身可能在外面。 为了防止这种情况,只需使用更多的填充。 例如,如果您的注释是20×20并且坐标居中,那么在所有边上使用10个更多的填充。
  2. 在iOS 7下面,地图不是缩放到完美的缩放比例,而是缩小到下一个大小(两个幂次)。 所以在注释周围会有更多的空间,正如截图所示。
  3. 在iOS 7上,地图视图不仅可以完美缩放,还可以自动关注状态栏。 为了使计算正确,您需要从iOS 7的顶部填充中减去状态栏高度。

从iOS 7.0开始,可以通过showAnnotations轻松实现。

迅速:

 mapView.showAnnotations(mapView.annotations, animated: true) 

Objective-C的:

 [mapView showAnnotations:mapView.annotations animated:YES]; 

上述语句将调整地图视图的可见矩形以显示所有注释。

如果你准备近似的计算,你可以做一些巧妙的缩放。

您的目标区域是380高的mapView,因此您需要比计算的区域高4.75倍的区域以适应您的注释。 (0.25以上,3.5以下)。

首先,您需要获得一个地区(或地图,您喜欢什么工作),并使其与您的目标可见区域相同的比例。 这是因为一个非常宽阔的区域不会触及可视区域的顶部和底部,因此乘以其高度不会使地图视图的顶部和底部触及某些东西。 因此,如果viewable_height/viewable_width > annotations_height/annotations_width ,则应将annotations_height设置为annotations_width * (viewable_height/viewable_width)

然后,你在标注框的北部添加25%,在南部添加350%。 您可以通过向南移动212.5%(当前高度)的中心并将垂直跨度增加475%来完成此操作。

现在,所有这些都是一个近似值,因为这个世界是球体,我们没有看平面投影(也就是赤道附近的1度纬度比极点附近小1度)。 但是,如果你想要准确的话,你可以考虑根据纬度等比例缩放数字。 如果你只处理城市规模的注释,你可能会好起来的。

希望有所帮助。

如果你想查找给定rect中的注释:

 - (NSArray*)allAnnotationsInMapRect:(MKMapRect)mapRect { NSMutableArray *annotationsInRect = [NSMutableArray array]; for(id<MKAnnotation *ann in self.allAnnotations) { MKMapPoint pt = MKMapPointForCoordinate(ann.coordinate); if(MKMapRectContainsPoint(mapRect, pt)) { [annotationsInRect addObject:ann]; } } return annotationsInRect; } 

并确保注释VIEWS正确,获取注释的区域,然后遍历它们,并获得每个视图的边界,看看边界是否适合在地图的visibleRect中,如果不修改区域!

~~像这样:

 - (void)assureAnnotationViewsAreVisible:(NSArray*)annotations originalRegion:(MKCoordinateRegion)originalRegion { CGFloat smallestX = MAXFLOAT; CGFloat smallestY = MAXFLOAT; CGFloat biggestX = -100; CGFloat biggestY = -100; //NSLog(@"---: %d", annotations.count); for(id<MKAnnotation> *annotation in annotations) { UIView *annotationView = [self.mapView viewForAnnotation:v]; CGRect annotationViewFrame = annotationView.bounds; annotationViewFrame.origin = [self.mapView convertCoordinate:annotationView.coordinate toPointToView:self.mapView]; annotationViewFrame.origin = CGPointMake(annotationViewFrame.origin.x-annotationViewFrame.size.width/2, annotationViewFrame.origin.y-annotationViewFrame.size.height); smallestX = MIN(annotationViewFrame.origin.x, smallestX); smallestY = MIN(annotationViewFrame.origin.y, smallestY); biggestX = MAX(annotationViewFrame.origin.x+annotationViewFrame.size.width, biggestX); biggestY = MAX(annotationViewFrame.origin.y+annotationViewFrame.size.height, biggestY); } //NSLog(@"---"); CGRect bounds = self.mapView.bounds; if(smallestX < bounds.origin.x || smallestY < bounds.origin.y || biggestX > bounds.origin.x+bounds.size.width || biggestY > bounds.origin.y+bounds.size.height) { CGRect neededRect = bounds; neededRect.origin = CGPointMake(MIN(bounds.origin.x, smallestX), MIN(bounds.origin.y, smallestY)); neededRect.size = CGSizeMake(MAX(bounds.size.width, biggestX), MAX(bounds.size.height, biggestY)); MKCoordinateRegion neededRegion = [self.mapView convertRect:neededRect toRegionFromView:self.mapView]; _ignoreRegionChange = YES; [self.mapView setRegion:originalRegion animated:NO]; _ignoreRegionChange = NO; [self.mapView setRegion:neededRegion animated:YES]; } else { MKCoordinateRegion currentRegion = self.mapView.region; _ignoreRegionChange = YES; [self.mapView setRegion:originalRegion animated:NO]; _ignoreRegionChange = NO; [self.mapView setRegion:currentRegion animated:YES]; } } 

尝试从lan和lon的所有注释边缘值(max和min)中获取值。

在开始时定义这个值:

 static float maxLat = FLT_MIN; static float maxLon = FLT_MIN; static float minLat = FLT_MAX; static float minLon = FLT_MAX; 

然后用这个函数来计算跨度和区域:

 - (void) zoomAndFit { for(int i = 0; i < [self.points count]; i++) { PRPlaceModel *place = [self.points objectAtIndex:i]; CLLocationCoordinate2D location; location.latitude = [place.lat floatValue]; location.longitude = [place.lon floatValue]; minLat = MIN(minLat, location.latitude); minLon = MIN(minLon, location.longitude); maxLat = MAX(maxLat, location.latitude); maxLon = MAX(maxLon, location.longitude); } MKCoordinateRegion region; MKCoordinateSpan span; span.latitudeDelta = 1.2*(maxLat - minLat); span.longitudeDelta = 1.2*(maxLon - minLon); CLLocationCoordinate2D location; location.latitude = (minLat + maxLat)/2; location.longitude = (minLon + maxLon)/2; region.span=span; region.center=location; [self.mapView setRegion:region animated:TRUE]; [self.mapView regionThatFits:region]; } 

并在viewDidLoad方法中使用它:

 -(void)viewDidLoad { [super viewDidLoad]; [self zoomAndFit]; }