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

sistema
fonte

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)
Carnal
fonte
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
quentinadam
fonte
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

Carteiro
fonte
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];
DerekH
fonte
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.

swennemen
fonte
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)
    }
}
Vlad Spreys
fonte
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.

zero3nna
fonte
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
usuario
fonte
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.

dispatchMain
fonte
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()
David T
fonte
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.

Gektron
fonte
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.

Tempo.
fonte
1

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

MKMapView center e zoom in

Ashu
fonte
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))
Vauxhall
fonte
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)
    }
}
Marcin Piela
fonte