【问题标题】:Display MKMapViewAnnotations within a map view's visible rect在地图视图的可见矩形内显示 MKMapViewAnnotations
【发布时间】:2013-02-16 23:00:17
【问题描述】:

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

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

【问题讨论】:

    标签: ios map mkmapview mapkit mkannotation


    【解决方案1】:

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

    斯威夫特:

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

    目标-C:

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

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

    【讨论】:

    • @kishan94 你使用的是 Swift 还是 Objective-C?
    • 我之前写的代码是针对 Swift 的。我添加了 Objective-C 的示例代码,请查看。
    • 好的 +1 已添加,抱歉,我没有在 Swift 中尝试过...@Andree
    【解决方案2】:

    设置地图区域以使所有注释都包含在MKMapView 的某个部分中,可以分三个步骤完成。输入是mapViewannotationsFrame

    1. 计算一个包含所有注释的MKMapRectmapRect
    2. 根据mapView.boundsannotationsFrame 计算内边距。
    3. 在地图视图上致电-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];
    }
    

    如果您选择了一个完美的位置(谁没有),请考虑以下三点:

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

    【讨论】:

      【解决方案3】:

      如果您准备近似计算,您可以使用一些巧妙的缩放来完成。

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

      首先,您需要获得一个区域(或 maprect,无论您喜欢在哪个区域工作),并使其与目标可视区域的比例相同。这是因为一个非常宽和短的区域不会触及可视区域的顶部和底部,因此乘以它的高度不会使某些东西触及地图视图的顶部和底部。所以如果viewable_height/viewable_width &gt; annotations_height/annotations_width你应该将annotations_height设置为annotations_width * (viewable_height/viewable_width)

      然后,您在注释框的北边添加 25%,在南边添加 350%。为此,您可以将中心向南移动 212.5%(当前高度)并将垂直跨度增加 475%。

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

      希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        如果要查找给定矩形中的注释:

        - (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];
            }
        }
        

        【讨论】:

        • 在 NSArray 中的 allAnnotations 以及您拥有的注释(在地图上或不在地图上)
        • MAXFLOAT 的值是多少?
        • 不知道。它是一个特定于平台/架构/操作系统的定义值.. 假设它是 ~32000
        【解决方案5】:

        您首先需要添加注释: (当然这是在你已经有了注释列表之后)

        Swift4:

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

        你可以使用 currentView 常量或者直接放置 MKMapRect 如下: (.visibleMapRect 返回:

        “地图视图当前显示的区域。”

        mapView.annotations(in: mapView.visibleMapRect)
        

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

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

            【讨论】:

            • 这段代码适合 mapView 范围内的所有注释,但我需要的是在 mapView 范围内适合给定 CGRect 中的所有注释。
            • @user2393462435 不清楚您到底想说什么...自己阅读并清楚您的要求。
            • @R.A mapView 大小为 320 x 380。设置 mapview 的区域,使所有注释都在矩形内(0.0、20.0、320.0、100.0)。就是这样。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-08-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-11
            • 2020-08-08
            • 1970-01-01
            相关资源
            最近更新 更多