我在路径样式视差表视图标题中显示 MKMapView。要创建效果,mapView 的边界要大于用户可见的区域。我需要设置地图视图区域,以便所有地图的注释都包含在 MKMapView 的可见矩形中。最好的方法是什么?
为清楚起见进行编辑:这是一个用例。mapView 大小为 320 x 380。然而,可见区域由 rect (0.0, 20.0, 320.0, 100.0) 定义。我需要设置区域,以便所有注释都出现在 mapView 的这个矩形中。
我在路径样式视差表视图标题中显示 MKMapView。要创建效果,mapView 的边界要大于用户可见的区域。我需要设置地图视图区域,以便所有地图的注释都包含在 MKMapView 的可见矩形中。最好的方法是什么?
为清楚起见进行编辑:这是一个用例。mapView 大小为 320 x 380。然而,可见区域由 rect (0.0, 20.0, 320.0, 100.0) 定义。我需要设置区域,以便所有注释都出现在 mapView 的这个矩形中。
设置地图区域以使所有注释都包含在某个部分中,MKMapView
可以通过三个步骤完成。输入是mapView
和annotationsFrame
。
MKMapRect
mapRect
包含所有注释的 an。mapView.bounds
从和计算填充插图annotationsFrame
。-setVisibleMapRect:edgePadding:animated:
地图视图。下面是一个测试的屏幕截图。红色叠加层显示annotationsFrame
.
这是代码。注意:这是一种简化将其添加到代码中的方法,并且未针对边缘情况进行测试,例如传入具有相同坐标的 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];
}
如果你想要一个完美的位置(谁没有),这里有三件事需要考虑:
annotationsFrame
,但注释本身可能在外部。为了防止这种情况,只需使用更多的填充。例如,如果您的注释是 20x20 并且以坐标为中心,则在所有边上再使用 10 个填充。从 iOS 7.0 开始,这可以通过showAnnotations
.
迅速:
mapView.showAnnotations(mapView.annotations, animated: true)
目标-C:
[mapView showAnnotations:mapView.annotations animated:YES];
上述语句将调整地图视图的可见矩形以显示所有注释。
如果您准备近似计算,您可以使用一些巧妙的缩放来完成。
您的目标区域是 380 的 mapView 中的 80 高。因此,您需要一个比计算出的适合您的注释的区域高 4.75 倍的区域。(以上额外 0.25,以下额外 3.5)。
首先,您需要获得一个区域(或 maprect,无论您喜欢在哪个区域工作)并使其与目标可视区域的比例相同。这是因为一个非常宽和短的区域不会触及可视区域的顶部和底部,因此乘以它的高度不会使某些东西触及地图视图的顶部和底部。所以如果viewable_height/viewable_width > annotations_height/annotations_width
你应该设置annotations_height
为annotations_width * (viewable_height/viewable_width)
.
然后,您在注释框的北边添加 25%,在南边添加 350%。您可以通过将中心向南移动 212.5%(当前高度)并将垂直跨度增加 475% 来做到这一点。
现在,考虑到世界是球体并且我们不是在看平面投影(即赤道附近的 1 度纬度小于两极附近的 1 度),所有这些都是近似值。但是如果你想要准确的话,你可以考虑根据纬度等来缩放数字。如果您只处理城市规模的注释,您可能会没事。
希望有帮助。
如果要查找给定矩形中的注释:
- (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;
}
并确保注释视图在矩形中,获取注释区域,然后遍历它们并获取每个视图的边界,看看边界是否适合地图的可见矩形,如果不修改该区域!
~~ 像这样:
- (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];
}
}
您首先需要添加注释:(当然这是在您已经拥有注释列表之后)
斯威夫特4:
self.mapView.addAnnotations(annotations)
let currentView = mapView.visibleMapRect
mapView.annotations(in: currentView)
您可以使用 currentView 常量或直接放置 MKMapRect 如下: (.visibleMapRect 返回:
“地图视图当前显示的区域。”
mapView.annotations(in: mapView.visibleMapRect)
我发现了一种不计算的更简单的方法是让地图视图计算它,然后我们调整边缘。
//1: Show all annotation on the map view, but without animation
self.mapView.showAnnotations(self.mapView.annotations, animated: false)
//2: Get the current visible map rect
let currentMapRect = self.mapView.visibleMapRect
//3: Create the edges inset that you want the map view to show all annotation within
let padding = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
//4: Set current map rect but with new padding, also set animation to true to see effect
self.mapView.setVisibleMapRect(currentMapRect, edgePadding: padding, animated: true)
尝试从 lan 和 lon 的所有注释边缘值(最大值和最小值)中获取。
在开头定义这个值:
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];
}