I\'m using MapKit on iPhone. How can I know when the user changes the zoom level (zoom in\\out the map)?
I\'ve tried to use mapView:(MKMapView *)mapView regi
I have the following MKMapView category in which I include a method for quickly getting the current zoom level for the map:
@implementation MKMapView (ZoomLevel)
- (NSUInteger) zoomLevel {
MKCoordinateRegion region = self.region;
double centerPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude];
double topLeftPixelX = [MKMapView longitudeToPixelSpaceX: region.center.longitude - region.span.longitudeDelta / 2];
double scaledMapWidth = (centerPixelX - topLeftPixelX) * 2;
CGSize mapSizeInPixels = self.bounds.size;
double zoomScale = scaledMapWidth / mapSizeInPixels.width;
double zoomExponent = log(zoomScale) / log(2);
double zoomLevel = 21 - zoomExponent;
return zoomLevel;
}
@end
To obtain the zoom level, you can call the following in your delegates and determine if the zoom level has changed:
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
NSUInteger zoomLevel = [mapView zoomLevel];
}
It is pretty simple to calculate the zoom level. See the snippet below. You can get the mRect parameter from the visibleMapRect
property on your MKMapView
instance.
+ (NSUInteger)zoomLevelForMapRect:(MKMapRect)mRect withMapViewSizeInPixels:(CGSize)viewSizeInPixels
{
NSUInteger zoomLevel = MAXIMUM_ZOOM; // MAXIMUM_ZOOM is 20 with MapKit
MKZoomScale zoomScale = mRect.size.width / viewSizeInPixels.width; //MKZoomScale is just a CGFloat typedef
double zoomExponent = log2(zoomScale);
zoomLevel = (NSUInteger)(MAXIMUM_ZOOM - ceil(zoomExponent));
return zoomLevel;
}
You could probably just stop at the step for calculating the zoomScale
as that will tell you if the zoom has changed at all.
I figured this stuff out from reading Troy Brants excellent blog posts on the topic:
http://troybrant.net/blog/2010/01/mkmapview-and-zoom-levels-a-visual-guide/
Swift 3
extension MKMapView {
var zoomLevel: Int {
let maxZoom: Double = 20
let zoomScale = self.visibleMapRect.size.width / Double(self.frame.size.width)
let zoomExponent = log2(zoomScale)
return Int(maxZoom - ceil(zoomExponent))
}
}
You could save a latitude delta, then when regionDidChangeAnimated:
is called, check to see if the new latitude delta is different. I think the latitude delta stays constant as long as the map isn't zoomed.
I found this very helpful and developed the following code based on these answers.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
mapRegion = self.mapView.region;
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{
MKCoordinateRegion newRegion = self.mapView.region;
NSInteger zFactor;
if ((mapRegion.span.latitudeDelta/newRegion.span.latitudeDelta) > 1.5){
NSLog(@"Zoom in changed");
zFactor = 20;
CustomPlacemark *aO;
MKAnnotationView *aV;
for (aO in self.mapView.annotations) {
aV = [[self mapView] viewForAnnotation:aO];
aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y, aV.frame.size.width+zFactor, aV.frame.size.height+zFactor);
[[[self mapView] viewForAnnotation:aO] setFrame:aV.frame];
}
}
if ((mapRegion.span.latitudeDelta/newRegion.span.latitudeDelta) < 0.75){
NSLog(@"Zoom out");
zFactor = -20;
CustomPlacemark *aO;
MKAnnotationView *aV;
for (aO in self.mapView.annotations) {
aV = [[self mapView] viewForAnnotation:aO];
aV.frame = CGRectMake(aV.frame.origin.x, aV.frame.origin.y, aV.frame.size.width+zFactor, aV.frame.size.height+zFactor);
[[[self mapView] viewForAnnotation:aO] setFrame:aV.frame];
}
}
}
Count zoom scale in MKMapView - Swift solution
I created following extension for MKMapView, so you can get a scale of zoom on map. The solution is similar as presented above but in Swift.
There is also additional function scaleWithPrecision(_:Int64)
for rounding that scale what allow to filter out f.ex. little zoom changes on MapView
extension MKMapView {
var scale: Double {
return self.scaleWithPrecision(1)
}
func scaleWithPrecision(precision: Int64) -> Double {
let mapBoundsWidth = Double(self.bounds.size.width)
let mapRectWidth:Double = self.visibleMapRect.size.width
let scale: Double = round(Double(precision)*mapBoundsWidth/mapRectWidth)/Double(precision)
return scale
}
}
Much more simpler answer:
The easiest way to get an Integer of the current zoom level, is by using the MapView function: regionDidChangeAnimated. This function recognizes every change in zoom and will give you the basis for the calculation of the zoom factor.
Just insert this function into your MapView class (works for Swift 3.0):
var mapView: MKMapView! = nil
...
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
let zoomWidth = mapView.visibleMapRect.size.width
let zoomFactor = Int(log2(zoomWidth)) - 9
print("...REGION DID CHANGE: ZOOM FACTOR \(zoomFactor)")
}
And you will get a zoomFactor value out of it, where 0 is the most near point you can zoom into the map and every higher value is a far far away zoom... :-)