Definir o nível de zoom para um MKMapView


118

Tenho um mapa que mostra corretamente, a única coisa que quero fazer agora é definir o nível de zoom quando ele carregar. Existe uma maneira de fazer isso?

obrigado

Respostas:


198

Eu encontrei uma solução, que é muito simples e funciona. Use MKCoordinateRegionMakeWithDistancepara definir a distância em metros verticalmente e horizontalmente para obter o zoom desejado. E é claro que, ao atualizar sua localização, você obterá as coordenadas corretas ou poderá especificá-lo diretamente na CLLocationCoordinate2Dinicialização, se for isso o que você precisa fazer:

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 deve ser a resposta selecionada. Tentei várias outras soluções propostas, mas nenhuma funcionou corretamente. Este código é simples e eficaz.
— Levi Roberts

1
Boa resposta. Porém, o zoom será diferente dependendo do tamanho da tela, não?
— Vinzius

1
Curiosamente, MKCoordinateRegionMakeWithDistanceainda está por aí em Swift. Esta solução funciona!
— LinusGeffarth

47

Com base no fato de que as linhas de longitude são espaçadas igualmente em qualquer ponto do mapa, há uma implementação muito simples para definir 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

Correções 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

Obrigado! Sim, você está certo, na verdade eu tirei o código do meu projeto onde era uma função ao invés de um acréscimo ao MKMapView. Acabei de editar o código para refletir sua correção.
— quentinadam

1
Qual é o reverso dessa fórmula, para calcular o nível de zoom atual?
— Nick

1
Acho que é o seguinte:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
— Nick

1
@devios, no nível de zoom 1, o mundo inteiro (360 °) cabe em 1 bloco de 256px de largura. No nível de zoom 2, o mundo inteiro (360 °) cabe em 2 blocos de 256px (512px). No nível de zoom 3, o mundo inteiro (360 °) se encaixa em 4 blocos de 256px (1024px) etc.
— quentinadam

31

Não está embutido, mas eu vi / usei este código. Isso permite que você use:

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

Nota: Este não é o meu código, eu não o escrevi, portanto, não posso assumir os créditos por ele


1
uau, é muito código, você pensaria que deveria estar integrado. obrigado. vai dar uma olhada em como é feito.
— sistema

1
Você pode obter o arquivo .m e .h, adicioná-lo ao seu projeto e, em seguida, referenciá-lo no controlador de visualização do mapa e usá-lo como se fosse um método no MKMapView, oh, que alegria as categorias!
— PostMan de

2
Não funcionou para mim, ele apenas exibe o mesmo nível de zoom de antes. Eu devo estar fazendo algo errado.
— sistema

17

Você também pode aplicar zoom usando MKCoordinateRegion e definindo seu delta de latitude e longitude de extensão. Abaixo está uma referência rápida e aqui está a referência do iOS. Não vai adiantar nada, mas deve permitir que você defina o zoom quando desenha o mapa.


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

Editar 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
Isso não fez diferença para mim, quando eu altero alguns valores, simplesmente não carrega o mapa.
— sistema

Você está definindo isso quando o mapa é carregado ou está tentando manipular após o carregamento ter ocorrido? Você está usando 1 ou um número menor como deltas? Só estou tentando entender os requisitos.
— DerekH

Eu configurei antes do tempo de execução. Testei valores acima e abaixo de 1.
— sistema

1
Boa resposta, mas tente mudar a latitude, longitude delta para 0,1 - é mais ampliado.
— Daniel Krzyczkowski

12

Uma implementação simples do Swift, se você usar tomadas.

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

Com base na resposta de @Carnal.


12

Implementação 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
Não estou 100% certo, mas acho que quando você cria o seu IBOutletpara o seu map, você o define como um em MapViewWithZoomvez de um simples MKMapView. Em seguida, você pode definir o nível de zoom com map.zoomLevel = 1oumap.zoomLevel = 0.5
— Zonker.in.Geneva

1
alguns comentários sobre isso seriam mais úteis. Está funcionando no Swift 3.
— nyxee

Ótima solução! Mas eu gosto mais como uma extensão, e há uma coisa estranha: para realmente diminuir o zoom, preciso diminuir em 2 comomapView.zoomLevel -= 2
— Alexander

7

Para o Swift 3 , é um avanço muito 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)
}

Basta definir o lat-, long-Meters <CLLocationDistance>e o mapView ajustará o nível de zoom aos seus valores.


O que você quer dizer com "o mapView ajustará o nível de zoom aos seus valores"? Presumo que o OP queira definir o nível de zoom sozinho ou como você faria isso com a entrada que você sugeriu?
— riper

6

Com base na ótima resposta de @AdilSoomro . Eu vim com isto:

@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 o seguinte fragmento de código ajude você.

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

Você pode escolher qualquer valor em vez de 0,5 para reduzir ou aumentar o nível de zoom. Usei esses métodos clicando em dois botões.


2

Uma resposta do Swift 2.0 utilizando NSUserDefaults para salvar e restaurar o zoom e a posição do mapa.

Função para salvar a posição do mapa e 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")
}

Execute a função sempre que o mapa for movido:

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

Função para salvar o zoom e a posição do 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)
    }
}

Adicione isto a viewDidLoad:

restoreMapRegion()

1

Eu sei que esta é uma resposta tardia, mas eu só queria abordar a questão de definir o nível de zoom sozinho. A resposta da goldmine é ótima, mas descobri que não está funcionando bem o suficiente em meu aplicativo.

Em uma inspeção mais próxima, a mina de ouro afirma que "as linhas de longitude são espaçadas igualmente em qualquer ponto do mapa". Isso não é verdade, na verdade são as linhas de latitude que estão espaçadas igualmente de -90 (pólo sul) a +90 (pólo norte). As linhas de longitude são espaçadas em sua largura máxima no equador, convergindo para um ponto nos pólos.

A implementação que adotei é, portanto, usar o cálculo de latitude da seguinte forma:

@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 ajude neste estágio final.


Ok, ignore o acima. Goldmine está correto, as linhas de longitude SÃO igualmente espaçadas porque é claro que a projeção de Mercator é usada para os mapas. Meus problemas com a solução originaram-se de outro pequeno bug em meu aplicativo relacionado à criação de subclasses da nova classe MKTileOverlay do iOS 7.
— gektron

Você pode querer considerar a atualização de sua postagem para refletir as informações que você incluiu em seu comentário.
— Derek Lee

1

Rápido:

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

locValue é a sua coordenada.


1

Aqui, eu coloco minha resposta e está funcionando para o rápido 4.2 .

MKMapView center e zoom in


Se eu clicar aqui e rolar para baixo, e clicar no seu link ali, eu me encontrarei aqui novamente e então clico aqui e agora estou preso em um loop infinito 😏
— Matthijs

@Matthijs eu corrigi o link. Por favor, verifique e vote na resposta.
— Ashu

0

Com base na resposta do 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))

Obrigado, parece bom quando o mapa está voltado para o norte. Mas e se você estiver girando o mapa? E se o ângulo de compressão for diferente de 0?
— pierre23

0

MKMapViewextensão com base nesta resposta (+ precisão do nível de zoom de ponto flutuante):

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)
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.