Configuración del nivel de zoom para MKMapView


118

Tengo un mapa que se muestra correctamente, lo único que quiero hacer ahora es establecer el nivel de zoom cuando se carga. ¿Hay alguna forma de hacer esto?

Gracias

Respuestas:


198

Encontré una solución, que es muy simple y funciona. Use MKCoordinateRegionMakeWithDistancepara establecer la distancia en metros vertical y horizontalmente para obtener el zoom deseado. Y luego, por supuesto, cuando actualice su ubicación, obtendrá las coordenadas correctas, o puede especificarlas directamente CLLocationCoordinate2Dal inicio, si eso es lo que necesita hacer:

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

Rápido:

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

3
Esta debería ser la respuesta seleccionada. Probé muchas de las otras soluciones propuestas, pero ninguna funcionó correctamente. Este código es simple y efectivo.
Levi Roberts

1
Buena respuesta. Sin embargo, el zoom será diferente según el tamaño de la pantalla, ¿no?
Vinzius

1
Curiosamente, MKCoordinateRegionMakeWithDistancetodavía existe en Swift. ¡Esta solución funciona!
LinusGeffarth

47

Basado en el hecho de que las líneas de longitud están espaciadas por igual en cualquier punto del mapa, existe una implementación muy simple para establecer centerCoordinate y 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

Correcciones menores:- (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]; }
Monobono

¡Gracias! Sí, tienes razón, de hecho saqué el código de mi proyecto donde era una función en lugar de una adición a MKMapView. Acabo de editar el código para reflejar su corrección.
quentinadam

1
¿Cuál es el reverso de esa fórmula para calcular el nivel de zoom actual?
Nick

1
Creo que es esto:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Nick

1
@devios, en el nivel de zoom 1, todo el mundo (360 °) cabe en 1 mosaico de 256 px de ancho. En el nivel de zoom 2, todo el mundo (360 °) cabe en 2 mosaicos de 256 px (512 px). En el nivel de zoom 3, todo el mundo (360 °) cabe en 4 mosaicos de 256px (1024px), etc.
quentinadam

31

No está integrado, pero he visto / usado este código. Esto le permite usar esto:

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

Nota: este no es mi código, no lo escribí, por lo que no puedo atribuirme el mérito


1
wow, es mucho código, pensarías que debería estar integrado. Gracias. Verá cómo se hace.
sistema

1
Puede obtener el archivo .my .h, agregarlo a su proyecto, luego hacer referencia a él en su controlador de vista de mapa y usarlo como si fuera un método en MKMapView, ¡oh, el placer de las categorías!
PostMan

2
No funcionó para mí, solo muestra el mismo nivel de zoom que antes. Debo estar haciendo algo mal.
sistema

17

También puede hacer zoom utilizando MKCoordinateRegion y estableciendo su delta de latitud y longitud. A continuación se muestra una referencia rápida y aquí está la referencia de iOS. No hará nada elegante, pero debería permitirle establecer el zoom cuando dibuje el mapa.


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

Edición 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];

1
Esto no hizo ninguna diferencia para mí, cuando cambio algunos valores, simplemente no carga el mapa.
sistema

¿Está configurando esto cuando se carga el mapa o está tratando de manipular después de que se haya realizado la carga? ¿Estás usando 1 o un número menor como tus deltas? Solo intento comprender los requisitos.
DerekH

Lo configuré antes del tiempo de ejecución. Probé valores por encima y por debajo de 1.
sistema

1
Buena respuesta, pero intente cambiar la latitud, la longitud delta a 0,1; se amplía más.
Daniel Krzyczkowski

12

Una implementación simple de Swift, si usa puntos de venta.

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

Basado en la respuesta de @ Carnal.


12

Implementación rápida

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)
    }
}

1
No estoy 100% seguro, pero supongo que cuando creas tu IBOutleta tu map, lo defines como un en MapViewWithZoomlugar de un simple MKMapView. Luego, puede configurar el nivel de zoom con map.zoomLevel = 1omap.zoomLevel = 0.5
Zonker.in.Geneva

1
algunos comentarios sobre esto serían más útiles. Está funcionando en Swift 3.
nyxee

¡Gran solución! Pero me gusta más como una extensión, y hay una cosa extraña: para realmente alejar la imagen, es necesario disminuir en 2 me gustamapView.zoomLevel -= 2
Alexander

7

Para Swift 3 es bastante rápido:

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)
}

Simplemente defina los metros de <CLLocationDistance>latitud y longitud y mapView ajustará el nivel de zoom a sus valores.


¿Qué quiere decir con "mapView ajustará el nivel de zoom a sus valores"? Supongo que el OP quiere establecer el nivel de zoom él mismo o ¿cómo lo haría dada la entrada que sugiere?
maduro

6

Basado en la gran respuesta de @ AdilSoomro . Se me ocurrió esto:

@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

3

Espero que el siguiente fragmento de código te ayude.

- (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];
}

Puede elegir cualquier valor en lugar de 0,5 para reducir o aumentar el nivel de zoom. He utilizado estos métodos al hacer clic en dos botones.


2

Una respuesta de Swift 2.0 que utiliza NSUserDefaults para guardar y restaurar el zoom y la posición del mapa.

Función para guardar la posición del mapa y el 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")
}

Ejecute la función cada vez que se mueva el mapa:

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

Función para guardar el zoom y la posición del mapa:

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)
    }
}

Agregue esto para viewDidLoad:

restoreMapRegion()

1

Sé que esta es una respuesta tardía, pero solo quería abordar el problema de configurar el nivel de zoom yo mismo. La respuesta de goldmine es excelente, pero descubrí que no funciona lo suficientemente bien en mi aplicación.

En una inspección más cercana, goldmine afirma que "las líneas de longitud están separadas por igual en cualquier punto del mapa". Esto no es cierto, de hecho son las líneas de latitud que están espaciadas equitativamente desde -90 (polo sur) a +90 (polo norte). Las líneas de longitud están espaciadas en su punto más ancho en el ecuador, convergiendo a un punto en los polos.

La implementación que he adoptado es, por lo tanto, usar el cálculo de latitud de la siguiente manera:

@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

Espero que te ayude en esta última etapa.


Ok ignora lo anterior. Goldmine tiene razón, las líneas de longitud ESTÁN igualmente espaciadas porque, por supuesto, se utiliza la proyección de Mercator para los mapas. Mis problemas con la solución surgieron de otro error menor en mi aplicación relacionado con la subclasificación de la nueva clase MKTileOverlay de iOS 7.
gektron

Es posible que desee considerar actualizar su publicación para reflejar la información que incluyó en su comentario.
Derek Lee

1

Rápido:

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

locValue es su coordenada.


1

Aquí, pongo mi respuesta y funciona para Swift 4.2 .

MKMapView centrar y acercar


Si hago clic aquí y me desplazo hacia abajo, y hago clic en su enlace allí, me encontraré aquí nuevamente y luego hago clic aquí y ahora estoy atrapado en un bucle infinito 😏
Matthijs

@Matthijs He corregido el enlace. Por favor marque y vote la respuesta.
Ashu

0

Basado en la respuesta de quentinadam

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))

Gracias, se ve bien cuando el mapa está orientado hacia el norte. Pero, ¿y si está rotando el mapa? ¿Qué pasa si el ángulo de pellizco es diferente a 0?
pierre23

0

MKMapViewextensión basada en esta respuesta (+ precisión de nivel de zoom de punto flotante):

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)
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.