开发者

Setting the zoom level for a MKMapView

I have a map which shows correctly, the only thing I开发者_运维问答 want to do now is set the zoom level when it loads. Is there a way to do this?

Thanks


I found myself a solution, which is very simple and does the trick. Use MKCoordinateRegionMakeWithDistance in order to set the distance in meters vertically and horizontally to get the desired zoom. And then of course when you update your location you'll get the right coordinates, or you can specify it directly in the CLLocationCoordinate2D at startup, if that's what you need to do:

CLLocationCoordinate2D noLocation;
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(noLocation, 500, 500);
MKCoordinateRegion adjustedRegion = [self.mapView regionThatFits:viewRegion];          
[self.mapView setRegion:adjustedRegion animated:YES];
self.mapView.showsUserLocation = YES;

Swift:

let location = ...
let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: CLLocationDistance(exactly: 5000)!, longitudinalMeters: CLLocationDistance(exactly: 5000)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)


Based on the fact that longitude lines are spaced apart equally at any point of the map, there is a very simple implementation to set the centerCoordinate and zoomLevel:

@interface MKMapView (ZoomLevel)

@property (assign, nonatomic) NSUInteger zoomLevel;

- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
                  zoomLevel:(NSUInteger)zoomLevel
                   animated:(BOOL)animated;

@end


@implementation MKMapView (ZoomLevel)

- (void)setZoomLevel:(NSUInteger)zoomLevel {
    [self setCenterCoordinate:self.centerCoordinate zoomLevel:zoomLevel animated:NO];
}

- (NSUInteger)zoomLevel {
    return log2(360 * ((self.frame.size.width/256) / self.region.span.longitudeDelta)) + 1;
}

- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
zoomLevel:(NSUInteger)zoomLevel animated:(BOOL)animated {
    MKCoordinateSpan span = MKCoordinateSpanMake(0, 360/pow(2, zoomLevel)*self.frame.size.width/256);
    [self setRegion:MKCoordinateRegionMake(centerCoordinate, span) animated:animated];
}

@end


It's not built in, but I've seen / used this code. This allows you to use this:

[mapView setCenterCoordinate:myCoord zoomLevel:13 animated:YES];

Note: This is not my code, I did not write it, so therefore can't take credit for it


You can also zoom by using MKCoordinateRegion and setting its span latitude & longitude delta. Below is a quick reference and here is the iOS reference. It won't do anything fancy but should allow you to set zoom when it draws the map.


MKCoordinateRegion region;
region.center.latitude = {desired lat};
region.center.longitude = {desired lng};
region.span.latitudeDelta = 1;
region.span.longitudeDelta = 1;
mapView.region = region;

Edit 1:

MKCoordinateRegion region;
region.center.latitude = {desired lat};
region.center.longitude = {desired lng};
region.span.latitudeDelta = 1;
region.span.longitudeDelta = 1;
region = [mapView regionThatFits:region];
[mapView setRegion:region animated:TRUE];


Swift implementation

import Foundation
import MapKit

class MapViewWithZoom: MKMapView {

    var zoomLevel: Int {
        get {
            return Int(log2(360 * (Double(self.frame.size.width/256) / self.region.span.longitudeDelta)) + 1);
        }

        set (newZoomLevel){
            setCenterCoordinate(coordinate:self.centerCoordinate, zoomLevel: newZoomLevel, animated: false)
        }
    }

    private func setCenterCoordinate(coordinate: CLLocationCoordinate2D, zoomLevel: Int, animated: Bool) {
        let span = MKCoordinateSpan(latitudeDelta: 0, longitudeDelta: 360 / pow(2, Double(zoomLevel)) * Double(self.frame.size.width) / 256)
        setRegion(MKCoordinateRegion(center: coordinate, span: span), animated: animated)
    }
}


A simple Swift implementation, if you use outlets.

@IBOutlet weak var mapView: MKMapView! {
    didSet {
        let noLocation = CLLocationCoordinate2D()
        let viewRegion = MKCoordinateRegionMakeWithDistance(noLocation, 500, 500)
        self.mapView.setRegion(viewRegion, animated: false)
    }
}

Based on @Carnal's answer.


For Swift 3 it's pretty fast forward:

private func setMapRegion(for location: CLLocationCoordinate2D, animated: Bool)
{
    let viewRegion = MKCoordinateRegionMakeWithDistance(location, <#T##latitudinalMeters: CLLocationDistance##CLLocationDistance#>, <#T##longitudinalMeters: CLLocationDistance##CLLocationDistance#>)
    MapView.setRegion(viewRegion, animated: animated)
}

Just define the lat-, long-Meters <CLLocationDistance> and the mapView will fit the zoom level to your values.


Based on @AdilSoomro's great answer. I have come up with this:

@interface MKMapView (ZoomLevel)
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
                  zoomLevel:(NSUInteger)zoomLevel
                   animated:(BOOL)animated;

-(double) getZoomLevel;
@end



@implementation MKMapView (ZoomLevel)

- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
                  zoomLevel:(NSUInteger)zoomLevel animated:(BOOL)animated {
    MKCoordinateSpan span = MKCoordinateSpanMake(0, 360/pow(2, zoomLevel)*self.frame.size.width/256);
    [self setRegion:MKCoordinateRegionMake(centerCoordinate, span) animated:animated];
}


-(double) getZoomLevel {
    return log2(360 * ((self.frame.size.width/256) / self.region.span.longitudeDelta));
}

@end


I hope following code fragment would help you.

- (void)handleZoomOutAction:(id)sender {
    MKCoordinateRegion newRegion=MKCoordinateRegionMake(mapView.region.center,MKCoordinateSpanMake(mapView.region.s       pan.latitudeDelta/0.5, mapView.region.span.longitudeDelta/0.5));
    [mapView setRegion:newRegion];
}


- (void)handleZoomInAction:(id)sender {
    MKCoordinateRegion newRegion=MKCoordinateRegionMake(mapView.region.center,MKCoordinateSpanMake(mapView.region.span.latitudeDelta*0.5, mapView.region.span.longitudeDelta*0.5));
    [mapView setRegion:newRegion];
}

You can choose any value in stead of 0.5 to reduce or increase zoom level. I have used these methods on click of two buttons.


Swift:

Map.setRegion(MKCoordinateRegion(center: locValue, latitudinalMeters: 200, longitudinalMeters: 200), animated: true)

locValue is your coordinate.


MKMapView extension based on this answer (+ floating-point zoom level accuracy):

import Foundation
import MapKit

extension MKMapView {
    var zoomLevel: Double {
        get {
            return log2(360 * (Double(self.frame.size.width / 256) / self.region.span.longitudeDelta)) + 1
        }

        set (newZoomLevel){
            setCenterCoordinate(coordinate:self.centerCoordinate, zoomLevel: newZoomLevel, animated: false)
        }
    }

    private func setCenterCoordinate(coordinate: CLLocationCoordinate2D, zoomLevel: Double, animated: Bool) {
        let span = MKCoordinateSpan(latitudeDelta: 0, longitudeDelta: 360 / pow(2, zoomLevel) * Double(self.frame.size.width) / 256)
        setRegion(MKCoordinateRegion(center: coordinate, span: span), animated: animated)
    }
}


A Swift 2.0 answer utilising NSUserDefaults to save and restore the map's zoom and position.

Function to save the map position and zoom:

func saveMapRegion() {
    let mapRegion = [
        "latitude" : mapView.region.center.latitude,
        "longitude" : mapView.region.center.longitude,
        "latitudeDelta" : mapView.region.span.latitudeDelta,
        "longitudeDelta" : mapView.region.span.longitudeDelta
    ]
    NSUserDefaults.standardUserDefaults().setObject(mapRegion, forKey: "mapRegion")
}

Run the function each time the map is moved:

func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) 
{
        saveMapRegion();
}

Function to save the map zoom and position:

func restoreMapRegion() 
{
    if let mapRegion = NSUserDefaults.standardUserDefaults().objectForKey("mapRegion") 
    {

        let longitude = mapRegion["longitude"] as! CLLocationDegrees
        let latitude = mapRegion["latitude"] as! CLLocationDegrees
        let center = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)

        let longitudeDelta = mapRegion["latitudeDelta"] as! CLLocationDegrees
        let latitudeDelta = mapRegion["longitudeDelta"] as! CLLocationDegrees
        let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)

        let savedRegion = MKCoordinateRegion(center: center, span: span)

        self.mapView.setRegion(savedRegion, animated: false)
    }
}

Add this to viewDidLoad:

restoreMapRegion()


I know this is a late reply, but I've just wanted to address the issue of setting the zoom level myself. goldmine's answer is great but I found it not working sufficiently well in my application.

On closer inspection goldmine states that "longitude lines are spaced apart equally at any point of the map". This is not true, it is in fact latitude lines that are spaced equally from -90 (south pole) to +90 (north pole). Longitude lines are spaced at their widest at the equator, converging to a point at the poles.

The implementation I have adopted is therefore to use the latitude calculation as follows:

@implementation MKMapView (ZoomLevel)

- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate
    zoomLevel:(NSUInteger)zoom animated:(BOOL)animated
{
    MKCoordinateSpan span = MKCoordinateSpanMake(180 / pow(2, zoom) * 
        self.frame.size.height / 256, 0);
    [self setRegion:MKCoordinateRegionMake(coordinate, span) animated:animated];
}

@end

Hope it helps at this late stage.


Here, I put my answer and its working for swift 4.2.

MKMapView center and zoom in


Based on quentinadam's answer

Swift 5.1

// size refers to the width/height of your tile images, by default is 256.0
// Seems to get better results using round()
// frame.width is the width of the MKMapView

let zoom = round(log2(360 * Double(frame.width) / size / region.span.longitudeDelta))
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜