Ustawianie poziomu powiększenia dla MKMapView

118

Mam mapę, która pokazuje się poprawnie, jedyne, co chcę teraz zrobić, to ustawić poziom powiększenia podczas ładowania. Czy jest na to sposób?

Dzięki

system
źródło

Odpowiedzi:

200

Znalazłem rozwiązanie, które jest bardzo proste i załatwia sprawę. Użyj MKCoordinateRegionMakeWithDistance, aby ustawić odległość w metrach w pionie i poziomie, aby uzyskać żądany zoom. A potem, oczywiście, kiedy zaktualizujesz swoją lokalizację, otrzymasz właściwe współrzędne lub możesz określić je bezpośrednio w CLLocationCoordinate2Dprzy starcie, jeśli to jest to, co musisz zrobić:

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

Szybki:

let location = ...
let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: CLLocationDistance(exactly: 5000)!, longitudinalMeters: CLLocationDistance(exactly: 5000)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)
Cielesny
źródło
3
To powinna być wybrana odpowiedź. Wypróbowałem wiele innych proponowanych rozwiązań, ale żadne z nich nie działało poprawnie. Ten kod jest prosty i skuteczny.
Levi Roberts
1
Niezła odpowiedź. Jednak powiększenie będzie się różnić w zależności od rozmiaru ekranu, prawda?
Vinzius
1
Co ciekawe, MKCoordinateRegionMakeWithDistancewciąż jest w Swift. To rozwiązanie działa!
LinusGeffarth
47

Biorąc pod uwagę fakt, że linie długości geograficznej są równomiernie oddalone od siebie w dowolnym punkcie mapy, istnieje bardzo prosta implementacja ustawiania centerCoordinate i 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
źródło
Drobne poprawki:- (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
Dzięki! Tak, masz rację, faktycznie usunąłem kod z mojego projektu, w którym była to funkcja, a nie dodatek do MKMapView. Właśnie zredagowałem kod, aby odzwierciedlić twoją poprawkę.
quentinadam
1
Jaka jest odwrotność tego wzoru, aby obliczyć aktualny poziom powiększenia?
Nick
1
Myślę, że to jest to:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Nick
1
@devios, przy 1 poziomie powiększenia, cały świat (360 °) mieści się w 1 płytce o szerokości 256 pikseli. Na poziomie powiększenia 2 cały świat (360 °) mieści się w 2 kafelkach o wielkości 256 pikseli (512 pikseli). Na 3 poziomie powiększenia cały świat (360 °) mieści się w 4 kafelkach po 256px (1024px) itd.
quentinadam
31

Nie jest wbudowany, ale widziałem / użyłem tego kodu. Pozwala to na użycie tego:

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

Uwaga: to nie jest mój kod, nie napisałem go, więc nie mogę tego przypisać

Listonosz
źródło
1
wow, to dużo kodu, myślisz, że powinien być wbudowany. dzięki. spojrzy, jak to się robi.
system
1
Możesz pobrać plik .m i .h, dodać go do swojego projektu, a następnie odwołać się do niego w kontrolerze widoku mapy i używać go tak, jakby to była metoda na MKMapView, och, radość z kategorii!
PostMan
2
Nie działało dla mnie, po prostu wyświetla ten sam poziom powiększenia co wcześniej. Muszę robić coś złego.
system
17

Możesz również powiększyć, używając MKCoordinateRegion i ustawiając jego deltę szerokości i długości geograficznej. Poniżej znajduje się krótkie odniesienie, a tutaj odniesienie do systemu iOS. Nie zrobi nic wymyślnego, ale powinien pozwolić ci ustawić powiększenie podczas rysowania mapy.


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

Edycja 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
źródło
1
Nie zrobiło to dla mnie różnicy, kiedy zmieniam niektóre wartości, po prostu nie ładuje mapy.
system
Czy ustawiasz to, gdy mapa się ładuje, czy próbujesz manipulować po załadowaniu? Czy używasz 1 lub mniejszej liczby jako swoich delt? Próbuję tylko zrozumieć wymagania.
DerekH
Ustawiłem to przed uruchomieniem. Przetestowałem wartości powyżej i poniżej 1.
system
1
Dobra odpowiedź, ale spróbuj zmienić szerokość i deltę długości geograficznej na 0,1 - jest bardziej przybliżony.
Daniel Krzyczkowski
12

Prosta implementacja Swift, jeśli używasz gniazd.

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

Na podstawie odpowiedzi @ Carnal.

swennemen
źródło
12

Szybka realizacja

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
źródło
1
Nie jestem w 100% pewien, ale myślę, że kiedy tworzysz swój IBOutletdo swojego map, definiujesz go jako MapViewWithZoomzamiast prostego MKMapView. Następnie możesz po prostu ustawić poziom powiększenia za pomocą map.zoomLevel = 1lubmap.zoomLevel = 0.5
Zonker.in.Geneva
1
kilka komentarzy na ten temat byłoby bardziej pomocne. Działa w języku Swift 3.
nyxee,
Świetne rozwiązanie! Ale podoba mi się bardziej jako rozszerzenie i jest jedna dziwna rzecz: aby faktycznie pomniejszyć, należy zmniejszyć o 2, tak jakmapView.zoomLevel -= 2
Alexander
7

W przypadku Swift 3 jest dość szybko do przodu:

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

Wystarczy zdefiniować szerokość i długość metrów, <CLLocationDistance>a mapView dopasuje poziom powiększenia do Twoich wartości.

zero3nna
źródło
Co masz na myśli, mówiąc „mapView dopasuje poziom powiększenia do Twoich wartości”? Zakładam, że OP chce sam ustawić poziom powiększenia lub jak byś to zrobił, biorąc pod uwagę sugerowane dane wejściowe?
bardziej dojrzałe
6

Na podstawie świetnej odpowiedzi @ AdilSoomro . Wymyśliłem to:

@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
Nacięcie
źródło
3

Mam nadzieję, że poniższy fragment kodu Ci pomoże.

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

Możesz wybrać dowolną wartość zamiast 0,5, aby zmniejszyć lub zwiększyć poziom powiększenia. Użyłem tych metod po kliknięciu dwóch przycisków.

dispatchMain
źródło
2

Odpowiedź Swift 2.0 wykorzystująca NSUserDefaults do zapisywania i przywracania powiększenia i pozycji mapy.

Funkcja zapisywania pozycji mapy i powiększania:

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

Uruchom funkcję za każdym razem, gdy mapa jest przenoszona:

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

Funkcja zapisywania powiększenia i pozycji mapy:

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

Dodaj to do widokuDidLoad:

restoreMapRegion()
David T.
źródło
1

Wiem, że to spóźniona odpowiedź, ale po prostu chciałem sam rozwiązać problem ustawiania poziomu powiększenia. Odpowiedź goldmine jest świetna, ale okazało się, że nie działa wystarczająco dobrze w mojej aplikacji.

Przy bliższym przyjrzeniu się Goldmine stwierdza, że ​​„linie długości geograficznej są równomiernie oddalone od siebie w każdym punkcie mapy”. To nieprawda, w rzeczywistości są to linie szerokości geograficznej rozmieszczone w równych odstępach od -90 (biegun południowy) do +90 (biegun północny). Linie długości geograficznej są rozmieszczone w najszerszych odstępach na równiku, zbiegając się do punktu na biegunach.

Wdrożenie, które przyjąłem, polega zatem na zastosowaniu obliczenia szerokości geograficznej w następujący sposób:

@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

Mam nadzieję, że to pomoże na tak późnym etapie.

gektron
źródło
Ok zignoruj ​​powyższe. Goldmine jest poprawne, linie długości geograficznej są równomiernie rozmieszczone, ponieważ oczywiście na mapach używana jest odwzorowanie Mercatora. Moje problemy z rozwiązaniem wynikały z innego drobnego błędu w mojej aplikacji, związanego z podklasą nowej klasy iOS 7 MKTileOverlay.
gektron
Możesz rozważyć aktualizację swojego posta, aby odzwierciedlić informacje zawarte w komentarzu.
Derek Lee
1

Szybki:

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

locValue to Twoje współrzędne.

czas.
źródło
1

Tutaj umieściłem moją odpowiedź i działa na szybkie 4.2 .

MKMapWyświetl centrum i powiększ

Ashu
źródło
Jeśli kliknę tutaj i przewiń w dół, i kliknę tam twój link, znajdę się tutaj ponownie, a potem kliknę tutaj i teraz utknąłem w nieskończonej pętli 😏
Matthijs
@Matthijs Poprawiłem link. Sprawdź i zagłosuj na odpowiedź.
Ashu
0

Na podstawie odpowiedzi quentinadama

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
źródło
Dziękuję, wygląda dobrze, gdy mapa jest skierowana na północ. Ale co, jeśli obracasz mapę? Co się stanie, jeśli kąt zaciśnięcia jest inny niż 0?
pierre23
0

MKMapViewrozszerzenie oparte na tej odpowiedzi (+ dokładność powiększenia zmiennoprzecinkowego):

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
źródło