Is there way to limit MKMapView maximum zoom level?
the question is - is there a way to limit maximum zoom level for MKMapView? Or is there a way to track when user zooms to the level where there's no map image available?
If you're working with iOS 7+ only, there's a new camera.altitude
property that you can get/set to enforce a zoom level. Its equivalent to azdev's solution, but no external code is required.
In testing, I also discovered that it was possible to enter an infinite loop if you repeatedly tried to zoom in at detail, so I have a var to prevent that in my code below.
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
// enforce maximum zoom level
if (_mapView.camera.altitude < 120.00 && !_modifyingMap) {
_modifyingMap = YES; // prevents strange infinite loop case
_mapView.camera.altitude = 120.00;
_modifyingMap = NO;
}
}
You could use the mapView:regionWillChangeAnimated:
delegate method to listen for region change events, and if the region is wider than your maximum region, set it back to the max region with setRegion:animated:
to indicate to your user that they can't zoom out that far. Here's the methods:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated
I just spent some time working on this for an app i'm building. Here's what I came up with:
I started with Troy Brant's script on this page which is a nicer way to set the map view I think.
-
I added a method to return the current zoom level.
In MKMapView+ZoomLevel.h:
- (double)getZoomLevel;
In MKMapView+ZoomLevel.m:
// Return the current map zoomLevel equivalent, just like above but in reverse - (double)getZoomLevel{ MKCoordinateRegion reg=self.region; // the current visible region MKCoordinateSpan span=reg.span; // the deltas CLLocationCoordinate2D centerCoordinate=reg.center; // the center in degrees // Get the left and right most lonitudes CLLocationDegrees leftLongitude=(centerCoordinate.longitude-(span.longitudeDelta/2)); CLLocationDegrees rightLongitude=(centerCoordinate.longitude+(span.longitudeDelta/2)); CGSize mapSizeInPixels = self.bounds.size; // the size of the display window // Get the left and right side of the screen in fully zoomed-in pixels double leftPixel=[self longitudeToPixelSpaceX:leftLongitude]; double rightPixel=[self longitudeToPixelSpaceX:rightLongitude]; // The span of the screen width in fully zoomed-in pixels double pixelDelta=abs(rightPixel-leftPixel); // The ratio of the pixels to what we're actually showing double zoomScale= mapSizeInPixels.width /pixelDelta; // Inverse exponent double zoomExponent=log2(zoomScale); // Adjust our scale double zoomLevel=zoomExponent+20; return zoomLevel; }
This method relies on a few private methods in the code linked above.
-
I added this in to my MKMapView delegate (as @vladimir recommended above)
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { NSLog(@"%f",[mapView getZoomLevel]); if([mapView getZoomLevel]<10) { [mapView setCenterCoordinate:[mapView centerCoordinate] zoomLevel:10 animated:TRUE]; } }
This has the effect of re-zooming if the user gets too far out. You can use regionWillChangeAnimated to prevent the map from 'bouncing' back in.
Regarding the looping comments above, it looks like this method only iterates once.
Yes, this is doable. First, extend MKMapView by using MKMapView+ZoomLevel.
Then, implement this in your MKMapViewDelegate:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
// Constrain zoom level to 8.
if( [mapView zoomLevel] < 8 )
{
[mapView setCenterCoordinate:mapView.centerCoordinate
zoomLevel:8
animated:NO];
}
}