eric
eric

Reputation: 4951

Zoom MKMapView to an MKAnnotationView's frame

I have a grouped pin that I want to zoom in on. Of course I know the coords of the pin, and have it's view rect. I just want to zoom the map to just the right region so that the cluster fully expands showing all pins (plus a bit of padding). What's a good way of doing this?

Sidenote:
In my setup the cluster pin will automatically be expanded into individual pins when the zoom level increases so I'm good there. What I need to know is how to set the MapView to a new region based on the frame and coords of the cluster pin.

enter image description here

Upvotes: 9

Views: 2306

Answers (3)

tanaschita
tanaschita

Reputation: 251

You can use:

mapView.showAnnotations(groupedAnnotations, animated: true)

This method will zoom in and position the map automatically so the annotations are all visible.

Upvotes: 6

Dmitry
Dmitry

Reputation: 386

When user taps the cluster pin you'll have a callback to

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)

Now you just need to cast your MKAnnotationView to MKClusterAnnotation, then you get access to the collection of its member pins:

if let clustered = view.annotation as? MKClusterAnnotation {
    clustered.memberAnnotations.forEach { (annotation) in
        // Calculate region boundaries
    }
}

Full solution with Nick's calculations:

if let clustered = view.annotation as? MKClusterAnnotation {
    var minLat = CLLocationDegrees(exactly: 90)!
    var maxLat = CLLocationDegrees(exactly: -90)!
    var minLong = CLLocationDegrees(exactly: 180)!
    var maxLong = CLLocationDegrees(exactly: -180)!
    clustered.memberAnnotations.forEach { (annotation) in
        let coordinate = annotation.coordinate
        minLat = min(minLat, coordinate.latitude)
        maxLat = max(maxLat, coordinate.latitude)
        minLong = min(minLong, coordinate.longitude)
        maxLong = max(maxLong, coordinate.longitude)
    }

    let centerLat = (minLat + maxLat) / 2
    let centerLong = (minLong + maxLong) / 2
    let center = CLLocationCoordinate2D(latitude: centerLat, longitude: centerLong)
    let span = MKCoordinateSpan(latitudeDelta: (maxLat - minLat) * 1.5, longitudeDelta: (maxLong - minLong) * 1.5) // with some padding
    let region = MKCoordinateRegion(center: center, span: span)

    <your MKMapView>.setRegion(region, animated: true)
}

Upvotes: 3

Nick C
Nick C

Reputation: 645

Start by removing the group pin

[mapView removeAnnotation:groupAnnotation];

Then add the pins in the cluster

[mapView addAnnotations:clusterAnnotations];

Then determine the region to zoom to

CLLocationDegrees minLat = 90;
CLLocationDegrees maxLat = -90;
CLLocationDegress minLong = 180;
CLLocationDegrees maxLong = -180
[clusterAnnotations enumerateUsingBlock:^(id<MKAnnotation> annotation, NSUInteger idx, BOOL *stop) {
    CLLocationCoordinate2D coordinate = annotation.coordinate;
    minLat = MIN(minLat, coordinate.latitude);
    maxLat = MAX(maxLat, coordinate.latitude);
    minLong = MIN(minLong, coordinate.longitude);
    maxLong = MAX(maxLong, coordinate.longitude);
}
CLLocationCoordinate2D center = CLLocationCoordinate2DMake((minLat + maxLat)/2.f, (minLong + maxLong)/2.f);
MKCoordinateSpan span = MKCoordinateSpanMake((maxLat - minLat)*1.25, (maxLong - minLong)*1.25); //1.25 is for padding
MKCoordinateRegion region = MKCoordinateRegionMake(center, span);
[mapView setRegion:[mapView regionThatFits:region] animated:YES];

Upvotes: 1

Related Questions