Positioning MKMapView to show multiple annotations at once
Solution 1
As of iOS7 you can use showAnnotations:animated:
[mapView showAnnotations:annotations animated:YES];
Solution 2
The link posted by Jim is now dead, but i was able to find the code (which I had bookmarked somewhere). Hope this helps.
- (void)zoomToFitMapAnnotations:(MKMapView *)mapView {
if ([mapView.annotations count] == 0) return;
CLLocationCoordinate2D topLeftCoord;
topLeftCoord.latitude = -90;
topLeftCoord.longitude = 180;
CLLocationCoordinate2D bottomRightCoord;
bottomRightCoord.latitude = 90;
bottomRightCoord.longitude = -180;
for(id<MKAnnotation> annotation in mapView.annotations) {
topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);
bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
}
MKCoordinateRegion region;
region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
// Add a little extra space on the sides
region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1;
region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1;
region = [mapView regionThatFits:region];
[mapView setRegion:region animated:YES];
}
Solution 3
Why so complicated?
MKCoordinateRegion coordinateRegionForCoordinates(CLLocationCoordinate2D *coords, NSUInteger coordCount) {
MKMapRect r = MKMapRectNull;
for (NSUInteger i=0; i < coordCount; ++i) {
MKMapPoint p = MKMapPointForCoordinate(coords[i]);
r = MKMapRectUnion(r, MKMapRectMake(p.x, p.y, 0, 0));
}
return MKCoordinateRegionForMapRect(r);
}
Solution 4
I have done something similiar to this to zoom out (or in) to an area that included a point annotation and the current location. You could expand this by looping through your annotations.
The basic steps are:
- Calculate the min lat/long
- Calculate the max lat/long
- Create CLLocation objects for these two points
- Calculate distance between points
- Create region using center point
between points and distance converted
to degrees
- Pass region into MapView to adjust
- Use adjusted region to set MapView region
-(IBAction)zoomOut:(id)sender {
CLLocationCoordinate2D southWest = _newLocation.coordinate;
CLLocationCoordinate2D northEast = southWest;
southWest.latitude = MIN(southWest.latitude, _annotation.coordinate.latitude);
southWest.longitude = MIN(southWest.longitude, _annotation.coordinate.longitude);
northEast.latitude = MAX(northEast.latitude, _annotation.coordinate.latitude);
northEast.longitude = MAX(northEast.longitude, _annotation.coordinate.longitude);
CLLocation *locSouthWest = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude];
CLLocation *locNorthEast = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude];
// This is a diag distance (if you wanted tighter you could do NE-NW or NE-SE)
CLLocationDistance meters = [locSouthWest getDistanceFrom:locNorthEast];
MKCoordinateRegion region;
region.center.latitude = (southWest.latitude + northEast.latitude) / 2.0;
region.center.longitude = (southWest.longitude + northEast.longitude) / 2.0;
region.span.latitudeDelta = meters / 111319.5;
region.span.longitudeDelta = 0.0;
_savedRegion = [_mapView regionThatFits:region];
[_mapView setRegion:_savedRegion animated:YES];
[locSouthWest release];
[locNorthEast release];
}
Solution 5
I have a different answer. I was going to do implement the zoom-to-fit algorithm myself, but i figured that Apple must have a way to do what we wanted without so much work. Using the API doco quickly showed that I could use MKPolygon to do what was needed:
/* this simply adds a single pin and zooms in on it nicely */
- (void) zoomToAnnotation:(MapAnnotation*)annotation {
MKCoordinateSpan span = {0.027, 0.027};
MKCoordinateRegion region = {[annotation coordinate], span};
[mapView setRegion:region animated:YES];
}
/* This returns a rectangle bounding all of the pins within the supplied
array */
- (MKMapRect) getMapRectUsingAnnotations:(NSArray*)theAnnotations {
MKMapPoint points[[theAnnotations count]];
for (int i = 0; i < [theAnnotations count]; i++) {
MapAnnotation *annotation = [theAnnotations objectAtIndex:i];
points[i] = MKMapPointForCoordinate(annotation.coordinate);
}
MKPolygon *poly = [MKPolygon polygonWithPoints:points count:[theAnnotations count]];
return [poly boundingMapRect];
}
/* this adds the provided annotation to the mapview object, zooming
as appropriate */
- (void) addMapAnnotationToMapView:(MapAnnotation*)annotation {
if ([annotations count] == 1) {
// If there is only one annotation then zoom into it.
[self zoomToAnnotation:annotation];
} else {
// If there are several, then the default behaviour is to show all of them
//
MKCoordinateRegion region = MKCoordinateRegionForMapRect([self getMapRectUsingAnnotations:annotations]);
if (region.span.latitudeDelta < 0.027) {
region.span.latitudeDelta = 0.027;
}
if (region.span.longitudeDelta < 0.027) {
region.span.longitudeDelta = 0.027;
}
[mapView setRegion:region];
}
[mapView addAnnotation:annotation];
[mapView selectAnnotation:annotation animated:YES];
}
Hope this helps.
jbrennan
Cocoa & Cocoa touch developer, specializing in user interactions and usability. Developer and designer of Keener for iPhone & iPod touch.
Updated on July 08, 2022Comments
-
jbrennan almost 2 years
I've got several annotations I want to add to my MKMapView (it could 0-n items, where n is generally around 5). I can add the annotations fine, but I want to resize the map to fit all annotations onscreen at once, and I'm not sure how to do this.
I've been looking at
-regionThatFits:
but I'm not quite sure what to do with it. I'll post some code to show what I've got so far. I think this should be a generally straightforward task but I'm feeling a bit overwhelmed with MapKit so far.- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{ location = newLocation.coordinate; //One location is obtained.. just zoom to that location MKCoordinateRegion region; region.center = location; //Set Zoom level using Span MKCoordinateSpan span; span.latitudeDelta = 0.015; span.longitudeDelta = 0.015; region.span = span; // Set the region here... but I want this to be a dynamic size // Obviously this should be set after I've added my annotations [mapView setRegion:region animated:YES]; // Test data, using these as annotations for now NSArray *arr = [NSArray arrayWithObjects:@"one", @"two", @"three", @"four", nil]; float ex = 0.01; for (NSString *s in arr) { JBAnnotation *placemark = [[JBAnnotation alloc] initWithLat:(location.latitude + ex) lon:location.longitude]; [mapView addAnnotation:placemark]; ex = ex + 0.005; } // What do I do here? [mapView setRegion:[mapView regionThatFits:region] animated:YES]; }
Notice, this all happens as I receive a location update... I don't know if that's an appropriate place to do this. If not, where would be a better place?
-viewDidLoad
?Thanks in advance.