Search code examples
mkmapview

MKMapView Zoom to Annotations - annotationVisibleRect


Does anyone have an example of how to zoom an MKMapView to the area of all visible annotations using the annotationVisibleRect property on MKMapView? I have seen this post which offers a decent solution, but it seems that this annotationVisibleRect property would be the simplest solution.


Solution

  • Short answer: There is not a solution to this problem using annotationVisibleRect.

    There is no example because this property cannot be used in this way. The limited documentation provided for it is certainly misleading for someone who is looking for something convenient from MapKit to do a somewhat common task.

    annotationVisibleRect is the rect with regard to the MKAnnotationContainerView coordinate system. MKAnnotationContainerView is the superview for your annotations. If you look in MKMapView.h, you'll find this:

    // annotationVisibleRect is the visible rect where the annotations views are currently displayed.
    // The delegate can use annotationVisibleRect when animating the adding of the annotations views in mapView:didAddAnnotationViews:
    @property (nonatomic, readonly) CGRect annotationVisibleRect;
    

    Its specific purpose is for manipulation (animation) of the annotation views by providing a rectangle in their superview's coordinate system that matches the map view's viewport.

    You might think (as I did) that this or similar will do the trick:

    CGRect visibleRect = self.mapView.annotationVisibleRect;
    MKCoordinateRegion visibleRegion = [self.mapView convertRect:visibleRect toRegionFromView:self.mapView];
    [self.mapView setRegion:visibleRegion animated:YES];
    

    It won't. Calling setRegion:animated: may cause the application to crash because the "fromView" is the incorrect coordinate system and may cause the latitude or longitude to go over their min/max values. You'd actually have to do something like this:

    - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
    {
        if(views.count > 0) {
            MKAnnotationView *view = [views objectAtIndex:0];
            CGRect visibleRect = self.mapView.annotationVisibleRect;
            MKCoordinateRegion visibleRegion = [self.mapView convertRect:visibleRect toRegionFromView:view.superview];
            [self.mapView setRegion:visibleRegion animated:YES];
        }
    }
    

    This won't crash the application, but it won't change the region either. If you compare visibleRegion to self.mapView.region, you will find that they are identical. That is because the annotationVisibleRect represents the same area that is visible in the map view -- just in a different coordinate system to make it convenient for you to do things like make the map pins come flying in from the edge of the view. See this answer for details on how it is used.

    Also, for reference, here's where the MKAnnotationView sits in relation to MKMapView:

    MKMapView
    +-UIView
      +-MKScrollContainerView
        +-MKAnnotationContainerView    <-- coordinate system of annotationVisibleRect
          +-MKAnnotationView
    

    Hope that helps clear some things up -- if not, ask away.