ios 在Map视图的可见矩形内显示MKMapViewAnnotations

tvz2xvvm  于 2023-10-21  发布在  iOS
关注(0)|答案(8)|浏览(110)

我在路径样式的视差表视图标题中显示了一个MKMapView。要创建该效果,mapView边界要大于用户可见的区域。我需要设置Map视图区域,以便所有Map的注解都包含在MKMapView的可见矩形中。最好的办法是什么?

**为清晰起见,请编辑:**这是一个用例。mapView的大小为320 x 380。但是,可见区域由rect(0.0,20.0,320.0,100.0)定义。我需要设置区域,以便所有注解都出现在mapView中的这个矩形中。

0x6upsns

0x6upsns1#

设置Map区域,使所有注解都包含在MKMapView的某个部分中,可以通过三个步骤完成。输入是mapViewannotationsFrame
1.计算包含所有注解的MKMapRectmapRect
1.从mapView.boundsannotationsFrame计算填充插入。
1.在Map视图上调用-setVisibleMapRect:edgePadding:animated:
下面是一个测试的屏幕截图。红色覆盖显示annotationsFrame

这里是代码。注意:这是一个方法,可以简化将其添加到代码中的过程,并且它没有针对边缘情况进行测试,例如传入具有相同坐标的n个注解,或者注解相距太远以至于Map必须缩小太多,或者坐标跨越Map边缘+/-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 x20并以坐标为中心,则在所有边上使用10个以上的填充。
1.在iOS 7下,Map没有缩放到完美的缩放比例,而是缩放到下一个瓷砖大小(2的幂)。因此,注解周围的空间将比所需的更多,正如屏幕截图所示。
1.在iOS 7上,Map视图不仅可以完美缩放,还可以自动关注状态栏。要使计算正确,您需要从iOS 7上的顶部填充中减去状态栏高度。

blpfk2vs

blpfk2vs2#

从iOS 7.0开始,这可以通过showAnnotations轻松实现。
斯威夫特:

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

Objective-C:

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

上面的语句将调整Map视图的visible rect以显示所有注解。

llmtgqce

llmtgqce3#

我改进了凯尔麦的答案(https://stackoverflow.com/a/50663064/14745547
不同的是,凯尔的答案是不是动画从以前的位置。
要以动画方式移动,请执行以下操作:

extension MKMapView {
    func showAnnotationsWithPadding(_ annotations: [MKAnnotation], padding: UIEdgeInsets, animated: Bool) {
        // get the current rect
        let currentMapRect = self.visibleMapRect

        // show the annotations without animation to get the desired map rect
        self.showAnnotations(annotations, animated: false)
        
        // this is the rect we want to show
        let desiredMapRect = self.visibleMapRect
        
        // go back to previous rect to move with animation if wanted
        if animated {
            self.visibleMapRect = currentMapRect
        }

        // now we can show the annotations with padding
        self.setVisibleMapRect(desiredMapRect, edgePadding: padding, animated: animated)
    }
}
dba5bblo

dba5bblo4#

首先需要添加注解:(当然,这是在你已经有了一个注解列表之后)
迅捷4:

self.mapView.addAnnotations(annotations)
    let currentView = mapView.visibleMapRect
    mapView.annotations(in: currentView)

您可以使用currentView常量或直接将MKMapRect放置为:下面:(.visibleMapRect返回:
“Map视图当前显示的区域。”

mapView.annotations(in: mapView.visibleMapRect)
gwbalxhn

gwbalxhn5#

我发现了一个更简单的方法,不用计算,让Map视图计算它,然后我们调整边缘。

//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)
lnlaulya

lnlaulya6#

如果你准备近似计算,你可以用一些聪明的缩放来做。
你的目标区域是80高的Map视图是380。因此,您需要一个比计算的区域高4.75倍的区域,以适合您的注解。(以上0.25,以下3.5)。
首先,你需要得到一个区域(或mapret,无论你喜欢在哪里工作),并使其与你的目标可视区域相同的比例。这是因为一个非常宽和短的区域不会接触到可视区域的顶部和底部,因此将其高度相乘不会使其接触到Map视图的顶部和底部。所以如果viewable_height/viewable_width > annotations_height/annotations_width,你应该把annotations_height设置为annotations_width * (viewable_height/viewable_width)
这样,您就可以在注解框的北部添加25%,在南部添加350%。您可以通过将中心向南移动212.5%(当前高度)并将垂直跨度增加475%来完成此操作。
现在,所有这些都是一个近似值,因为世界是球体,我们不是在看平面投影(即。赤道附近的1度纬度小于两极附近的1度)。但是如果你想精确的话,你可以根据纬度等因素来调整数字。如果你只处理城市规模的注解,你可能会没事。
希望能帮上忙。

bq3bfh9z

bq3bfh9z7#

如果你想在给定的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在矩形中,获取标注的区域,然后遍历它们并获取每个视图的边界,看看边界是否适合Map的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];
    }
}
vktxenjb

vktxenjb8#

尝试从所有注解边中获取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];
}

相关问题