Réglage du niveau de zoom pour un MKMapView

118

J'ai une carte qui s'affiche correctement, la seule chose que je veux faire maintenant est de régler le niveau de zoom lors du chargement. Y a-t-il un moyen de faire cela?

Merci

système
la source

Réponses:

200

Je me suis trouvé une solution, qui est très simple et fait l'affaire. Utilisez MKCoordinateRegionMakeWithDistancepour régler la distance en mètres verticalement et horizontalement pour obtenir le zoom souhaité. Et puis bien sûr, lorsque vous mettez à jour votre emplacement, vous obtiendrez les bonnes coordonnées, ou vous pouvez le spécifier directement CLLocationCoordinate2Dau démarrage, si c'est ce que vous devez faire:

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

Rapide:

let location = ...
let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: CLLocationDistance(exactly: 5000)!, longitudinalMeters: CLLocationDistance(exactly: 5000)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)
Charnel
la source
3
Cela devrait être la réponse choisie. J'ai essayé beaucoup d'autres solutions proposées mais aucune d'elles n'a fonctionné correctement. Ce code est simple et efficace.
Levi Roberts
1
Bonne réponse. Cependant le zoom sera différent selon la taille de l'écran, non?
Vinzius
1
Fait intéressant, MKCoordinateRegionMakeWithDistanceest toujours là à Swift. Cette solution fonctionne!
LinusGeffarth
47

Sur la base du fait que les lignes de longitude sont espacées de manière égale en tout point de la carte, il existe une implémentation très simple pour définir le centerCoordinate et le 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
quentinadam
la source
Corrections mineures:- (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
Merci! Oui, vous avez raison, j'ai en fait sorti le code de mon projet où il s'agissait d'une fonction plutôt que d'un ajout à MKMapView. Je viens de modifier le code pour refléter votre correction.
quentinadam
1
Quel est l'inverse de cette formule, pour calculer le niveau de zoom actuel?
Nick
1
Je pense que c'est ça:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Nick
1
@devios, au niveau de zoom 1, le monde entier (360 °) tient dans 1 tuile de 256px de large. Au niveau de zoom 2, le monde entier (360 °) tient dans 2 tuiles de 256px (512px). Au niveau de zoom 3, le monde entier (360 °) tient dans 4 tuiles de 256px (1024px), etc.
Quentinadam
31

Ce n'est pas intégré, mais j'ai vu / utilisé ce code. Cela vous permet d'utiliser ceci:

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

Remarque: ce n'est pas mon code, je ne l'ai pas écrit, donc je ne peux pas m'en attribuer le mérite

Facteur
la source
1
wow, c'est beaucoup de code, vous penseriez qu'il devrait être intégré. merci. aura un coup d'oeil comment c'est fait.
système du
1
Vous pouvez obtenir le fichier .m et .h, l'ajouter à votre projet, puis le référencer dans votre contrôleur de vue cartographique, et l'utiliser comme s'il s'agissait d'une méthode sur MKMapView, oh les joies des catégories!
PostMan
2
Cela n'a pas fonctionné pour moi, il affiche simplement le même niveau de zoom qu'avant. Je dois faire quelque chose de mal.
système du
17

Vous pouvez également zoomer en utilisant MKCoordinateRegion et en définissant son delta de latitude et de longitude. Voici une référence rapide et voici la référence iOS. Il ne fera rien d'extraordinaire mais devrait vous permettre de régler le zoom lorsqu'il dessine la carte.


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

Modifier 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];
DerekH
la source
1
Cela n'a fait aucune différence pour moi, lorsque je change certaines valeurs, cela ne charge tout simplement pas la carte.
système du
Le définissez-vous lorsque la carte se charge ou essayez-vous de la manipuler après le chargement? Utilisez-vous 1 ou un nombre plus petit comme deltas? J'essaie juste de comprendre les exigences.
DerekH
Je l'ai défini avant l'exécution. J'ai testé des valeurs supérieures et inférieures à 1.
système du
1
Bonne réponse, mais essayez de changer la latitude, le delta de longitude à 0,1 - il est plus zoomé.
Daniel Krzyczkowski
12

Une implémentation simple de Swift, si vous utilisez des points de vente.

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

Basé sur la réponse de @ Carnal.

swennemen
la source
12

Mise en œuvre rapide

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)
    }
}
Vlad Spreys
la source
1
Je ne suis pas sûr à 100%, mais je suppose que lorsque vous créez votre IBOutletà votre map, vous le définissez comme un MapViewWithZoomau lieu d'un simple MKMapView. Ensuite, vous pouvez simplement régler le niveau de zoom avec map.zoomLevel = 1oumap.zoomLevel = 0.5
Zonker.in.Genève
1
quelques commentaires à ce sujet seraient plus utiles Cela fonctionne dans Swift 3.
nyxee
Excellente solution! Mais je l'aime plus en tant qu'extension, et il y a une chose étrange: pour faire un zoom arrière, il faut diminuer de 2 commemapView.zoomLevel -= 2
Alexander le
7

Pour Swift 3, c'est une avance assez rapide:

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

Définissez simplement les lat-, long-Meters <CLLocationDistance>et la mapView adaptera le niveau de zoom à vos valeurs.

zero3nna
la source
Qu'entendez-vous par "le mapView adaptera le niveau de zoom à vos valeurs"? Je suppose que l'OP veut régler le niveau de zoom lui-même ou comment feriez-vous cela compte tenu de l'entrée que vous suggérez?
riper
6

Basé sur l'excellente réponse de @ AdilSoomro . J'ai trouvé ceci:

@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
pseudo
la source
3

J'espère que le fragment de code suivant vous aidera.

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

Vous pouvez choisir n'importe quelle valeur au lieu de 0,5 pour réduire ou augmenter le niveau de zoom. J'ai utilisé ces méthodes en cliquant sur deux boutons.

expéditionMain
la source
2

Une réponse Swift 2.0 utilisant NSUserDefaults pour enregistrer et restaurer le zoom et la position de la carte.

Fonction pour enregistrer la position de la carte et zoomer:

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

Exécutez la fonction à chaque fois que la carte est déplacée:

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

Fonction pour enregistrer le zoom et la position de la carte:

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

Ajoutez ceci à viewDidLoad:

restoreMapRegion()
David T
la source
1

Je sais que c'est une réponse tardive, mais je voulais juste aborder la question du réglage du niveau de zoom moi-même. La réponse de goldmine est excellente mais je trouve que cela ne fonctionne pas suffisamment bien dans mon application.

En y regardant de plus près, Goldmine déclare que «les lignes de longitude sont espacées de manière égale en tout point de la carte». Ce n'est pas vrai, ce sont en fait des lignes de latitude espacées de façon égale de -90 (pôle sud) à +90 (pôle nord). Les lignes de longitude sont espacées à leur plus large à l'équateur, convergeant vers un point aux pôles.

La mise en œuvre que j'ai adoptée consiste donc à utiliser le calcul de latitude comme suit:

@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

J'espère que cela aide à ce stade tardif.

gektron
la source
Ok ignore ce qui précède. Goldmine est correct, les lignes de longitude SONT également espacées parce que bien sûr la projection Mercator est utilisée pour les cartes. Mes problèmes avec la solution provenaient d'un autre bogue mineur dans mon application lié au sous-classement de la nouvelle classe MKTileOverlay iOS 7.
gektron
Vous pouvez envisager de mettre à jour votre message pour refléter les informations que vous avez incluses dans votre commentaire.
Derek Lee
1

Rapide:

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

locValue est votre coordonnée.

temps.
la source
1

Ici, j'ai mis ma réponse et son fonctionnement pour swift 4.2 .

MKMapView centre et zoom avant

Ashu
la source
Si je clique ici et fais défiler vers le bas, et clique sur ton lien là-bas, je me retrouverai ici et puis je clique ici et maintenant je suis coincé dans une boucle infinie 😏
Matthijs
@Matthijs J'ai corrigé le lien. Veuillez vérifier et voter la réponse.
Ashu
0

Basé sur la réponse 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))
Vauxhall
la source
Merci, ça a l'air bien quand la carte fait face au nord. Mais que faire si vous faites pivoter la carte? Que faire si l'angle de pincement est différent de 0?
pierre23
0

MKMapViewextension basée sur cette réponse (+ précision du niveau de zoom en virgule flottante):

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)
    }
}
Marcin Piela
la source