Impostazione del livello di zoom per un MKMapView


118

Ho una mappa che viene visualizzata correttamente, l'unica cosa che voglio fare ora è impostare il livello di zoom quando viene caricata. C'è un modo per fare questo?

Grazie

Risposte:


198

Mi sono trovato una soluzione, che è molto semplice e fa il trucco. Utilizzare MKCoordinateRegionMakeWithDistanceper impostare la distanza in metri verticalmente e orizzontalmente per ottenere lo zoom desiderato. E poi ovviamente quando aggiorni la tua posizione otterrai le coordinate giuste, oppure puoi specificarlo direttamente CLLocationCoordinate2Dall'avvio, se è quello che devi fare:

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)

3
Questa dovrebbe essere la risposta selezionata. Ho provato molte altre soluzioni proposte ma nessuna ha funzionato correttamente. Questo codice è semplice ed efficace.
Levi Roberts

1
Bella risposta. Tuttavia lo zoom sarà diverso a seconda delle dimensioni dello schermo, no?
Vinzius

1
È interessante notare che MKCoordinateRegionMakeWithDistanceè ancora in giro in Swift. Questa soluzione funziona!
LinusGeffarth

47

Sulla base del fatto che le linee di longitudine sono distanziate equamente in qualsiasi punto della mappa, esiste un'implementazione molto semplice per impostare centerCoordinate e 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

Correzioni minori:- (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

Grazie! Sì, hai ragione, in realtà ho tolto il codice dal mio progetto in cui era una funzione piuttosto che un'aggiunta a MKMapView. Ho appena modificato il codice per riflettere la tua correzione.
Quentinadam

1
Qual è il contrario di quella formula, per calcolare il livello di zoom corrente?
Nick

1
Penso che sia questo:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Nick

1
@devios, al livello di zoom 1, il mondo intero (360 °) si adatta a 1 riquadro di 256 px di larghezza. Al livello di zoom 2, l'intero mondo (360 °) si inserisce in 2 riquadri di 256 px (512 px). Al livello di zoom 3, il mondo intero (360 °) si adatta a 4 riquadri di 256 px (1024 px), ecc.
quentinadam

31

Non è integrato, ma ho visto / usato questo codice. Questo ti permette di usare questo:

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

Nota: questo non è il mio codice, non l'ho scritto, quindi non posso prendermene il merito


1
wow, è un sacco di codice, penseresti che dovrebbe essere integrato. grazie. avrà uno sguardo come è fatto.
sistema

1
Puoi ottenere il file .m e .h, aggiungerlo al tuo progetto, quindi referenziarlo nel controller della visualizzazione mappa e usarlo come se fosse un metodo su MKMapView, oh gioia delle categorie!
PostMan

2
Non ha funzionato per me, mostra solo lo stesso livello di zoom di prima. Devo fare qualcosa di sbagliato.
sistema

17

Puoi anche eseguire lo zoom utilizzando MKCoordinateRegion e impostando la latitudine di span e il delta di longitudine. Di seguito è riportato un riferimento rapido ed ecco il riferimento iOS. Non farà nulla di stravagante, ma dovrebbe consentirti di impostare lo zoom quando disegna la mappa.


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

Modifica 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
Questo non ha fatto differenza per me, quando cambio alcuni valori, semplicemente non carica la mappa.
sistema

Lo stai impostando quando la mappa viene caricata o stai cercando di manipolare dopo che il caricamento è avvenuto? Stai usando 1 o un numero inferiore come delta? Sto solo cercando di capire i requisiti.
DerekH

L'ho impostato prima del runtime. Ho testato i valori sopra e sotto 1.
sistema

1
Buona risposta, ma prova a cambiare la latitudine, il delta della longitudine a 0,1: viene ingrandito di più.
Daniel Krzyczkowski

12

Una semplice implementazione di Swift, se utilizzi punti vendita.

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

Basato sulla risposta di @ Carnal.


12

Rapida implementazione

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
Non ne sono sicuro al 100%, ma immagino che quando crei il tuo IBOutletper il tuo map, lo definisci come un MapViewWithZoominvece di un semplice MKMapView. Quindi, puoi semplicemente impostare il livello di zoom con map.zoomLevel = 1omap.zoomLevel = 0.5
Zonker.in.Geneva

1
alcuni commenti su questo sarebbero più utili. Funziona in Swift 3.
nyxee

Ottima soluzione! Ma mi piace di più come estensione, e c'è una cosa strana: per ridurre effettivamente lo zoom, è necessario diminuire di 2 comemapView.zoomLevel -= 2
Alexander

7

Per Swift 3 è abbastanza veloce in avanti:

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

Basta definire lat-, long-Meter <CLLocationDistance>e mapView adatterà il livello di zoom ai tuoi valori.


Cosa intendi con "mapView adatterà il livello di zoom ai tuoi valori"? Presumo che l'OP voglia impostare il livello di zoom da solo o come lo faresti dato l'input che suggerisci?
più maturo

6

Basato sull'ottima risposta di @ AdilSoomro . Ho pensato a questo:

@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

Spero che il seguente frammento di codice ti possa aiutare.

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

È possibile scegliere qualsiasi valore invece di 0,5 per ridurre o aumentare il livello di zoom. Ho usato questi metodi facendo clic su due pulsanti.


2

Una risposta Swift 2.0 che utilizza NSUserDefaults per salvare e ripristinare lo zoom e la posizione della mappa.

Funzione per salvare la posizione sulla mappa e ingrandire:

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

Esegui la funzione ogni volta che la mappa viene spostata:

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

Funzione per salvare lo zoom e la posizione della mappa:

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

Aggiungi questo a viewDidLoad:

restoreMapRegion()

1

So che questa è una risposta tardiva, ma volevo solo affrontare il problema dell'impostazione del livello di zoom da solo. La risposta della miniera d'oro è ottima, ma ho trovato che non funziona abbastanza bene nella mia applicazione.

A un esame più attento, la miniera d'oro afferma che "le linee di longitudine sono equidistanti in qualsiasi punto della mappa". Questo non è vero, si tratta infatti di linee di latitudine che sono spaziate equamente da -90 (polo sud) a +90 (polo nord). Le linee di longitudine sono spaziate nel punto più ampio all'equatore, convergenti in un punto ai poli.

L'implementazione che ho adottato è quindi quella di utilizzare il calcolo della latitudine come segue:

@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

Spero che aiuti in questa fase avanzata.


Ok, ignora quanto sopra. Goldmine è corretto, le linee di longitudine SONO equidistanti perché ovviamente la proiezione di Mercatore è usata per le mappe. I miei problemi con la soluzione derivavano da un altro bug minore nella mia applicazione che aveva a che fare con la sottoclasse della nuova classe MKTileOverlay di iOS 7.
gektron

Potresti considerare di aggiornare il tuo post per riflettere le informazioni che hai incluso nel tuo commento.
Derek Lee

1

Swift:

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

locValue è la tua coordinata.


1

Qui, ho messo la mia risposta e il suo funzionamento per swift 4.2 .

MKMapView center e ingrandisci


Se clicco qui e scorro verso il basso e clicco sul tuo link lì, mi ritroverò di nuovo qui e poi clicco qui e ora sono bloccato in un ciclo infinito 😏
Matthijs

@ Matthijs ho corretto il collegamento. Si prega di controllare e votare la risposta.
Ashu

0

Basato sulla risposta di 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))

Grazie, sembra a posto quando la mappa è rivolta a nord. Ma cosa succede se stai ruotando la mappa? Cosa succede se l'angolo di presa è diverso da 0?
pierre23

0

MKMapViewestensione basata su questa risposta (+ precisione del livello di zoom in virgola mobile):

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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.