Display MKMapViewAnnotations within a map view's visible rect

后端 未结 7 642
梦毁少年i
梦毁少年i 2021-02-02 02:31

I\'m displaying an MKMapView inside a Path-style parallax table view header. To create the effect, the mapView bounds is larger than the area visible to the user. I need to set

相关标签:
7条回答
  • 2021-02-02 02:59

    Try to get from all your annotation edges value (max and min) for lan and lon.

    Define this value on the beginning:

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

    and then use this function to calculate span and region:

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

    And use it in viewDidLoad method:

    -(void)viewDidLoad
    {
        [super viewDidLoad];
        [self zoomAndFit];
    }
    
    0 讨论(0)
  • I found an easier way without calculating is let the map view calculate it, then we adjust the edges.

    //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)
    
    0 讨论(0)
  • 2021-02-02 03:07

    Starting from iOS 7.0, this can be easily achieved with showAnnotations.

    Swift:

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

    Objective-C:

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

    The above statement will adjust the map view's visible rect in order to display all annotations.

    0 讨论(0)
  • 2021-02-02 03:08

    Setting the map region so that all annotations are contained in a certain part of an MKMapView can be done in three steps. Input are the mapView and the annotationsFrame.

    1. Calculate an MKMapRect mapRect that contains all annotations.
    2. Calculate the padding insets from mapView.bounds and annotationsFrame.
    3. Call -setVisibleMapRect:edgePadding:animated: on the map view.

    Below is a screen shot of a test. The red overlay shows the annotationsFrame.

    screen Shot of a test showing that all annotations are inside the given annotation frame

    Here is the code. Beware: It's all in one method to simplify adding it to your code, and it is not tested for edge cases like passing in n annotations with the same coordinate, or having the annotations so far apart that the map would have to get zoomed out too much, or having coordinates that span the edge of the map at +/-180 degrees longitude.

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

    If you go for a perfect placement (and who doesn't), here are three things to consider:

    1. The code assures that all the coordinates are in the annotationsFrame, but the annotations themselves may be outside. To prevent that, simply use more padding. For example, if your annotations are 20x20 and centered on the coordinate, use 10 more padding on all sides.
    2. Below iOS 7, the map was not zooming to the perfect zoom scale, but to the next tile size (power of two). So there will be more space around the annotations than needed, just as shown on the screenshot.
    3. On iOS 7, the map view will not only zoom perfectly, but automatically care about the status bar. To make the calculation correct, you need to subtract the status bar height from the top padding on iOS 7.
    0 讨论(0)
  • 2021-02-02 03:13

    if you want to find the annotations that are in a given 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;
    }
    

    and to assure the annotation VIEWS are in the rect, get the region for the annotations, then walk through them and get each view's bounds see if the bounds fit inside the visibleRect of the map and if not modify the region!

    ~~ like this:

    - (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];
        }
    }
    
    0 讨论(0)
  • 2021-02-02 03:20

    You first need to add the annotations: (of course this is after you already have a list of annotations)

    Swift4:

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

    You can use the currentView constant or directly place the MKMapRect as such: Below: (.visibleMapRect returns:

    "The area currently displayed by the map view."

    mapView.annotations(in: mapView.visibleMapRect)
    
    0 讨论(0)
提交回复
热议问题