Установка уровня масштабирования для MKMapView


118

У меня есть карта, которая отображается правильно, единственное, что я хочу сейчас сделать, это установить уровень масштабирования при загрузке. Есть ли способ сделать это?

Спасибо

Ответы:


200

Я нашел себе решение, которое очень простое и помогает. Используйте MKCoordinateRegionMakeWithDistanceдля установки расстояния в метрах по вертикали и горизонтали, чтобы получить желаемый масштаб. И, конечно же, когда вы обновите свое местоположение, вы получите правильные координаты, или вы можете указать их прямо CLLocationCoordinate2Dпри запуске, если это то, что вам нужно сделать:

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
Это должен быть выбранный ответ. Я пробовал много других предложенных решений, но ни одно из них не сработало должным образом. Этот код прост и эффективен.
Леви Робертс

1
Хороший ответ. Однако масштабирование будет разным в зависимости от размера экрана, не так ли?
Винциус

1
Интересно, MKCoordinateRegionMakeWithDistanceчто в Swift все еще присутствует. Это решение работает!
LinusGeffarth

47

Основываясь на том факте, что линии долготы разнесены одинаково в любой точке карты, существует очень простая реализация для установки centerCoordinate и 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

Мелкие исправления:- (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

Спасибо! Да, вы правы, я фактически взял код из своего проекта, где он был функцией, а не дополнением к MKMapView. Я только что отредактировал код, чтобы отразить ваше исправление.
quentinadam

1
Какова обратная сторона этой формулы для определения текущего уровня масштабирования?
Ник

1
Думаю, это так:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Ник

1
@devios, при уровне масштабирования 1 весь мир (360 °) умещается в 1 плитку шириной 256 пикселей. На уровне масштабирования 2 весь мир (360 °) умещается в 2 тайла по 256 пикселей (512 пикселей). На уровне масштабирования 3 весь мир (360 °) умещается в 4 плитки размером 256 пикселей (1024 пикселей) и т. Д.
quentinadam

31

Он не встроен, но я видел / использовал этот код. Это позволяет вам использовать это:

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

Примечание: это не мой код, я его не писал, поэтому не могу брать на себя ответственность


1
вау, здесь много кода, и можно подумать, что он должен быть встроен. Спасибо. Посмотрим, как это делается.
system

1
Вы можете получить файлы .m и .h, добавить их в свой проект, затем сослаться на них в контроллере представления карты и использовать его, как если бы это был метод MKMapView, о радости категорий!
PostMan

2
У меня не сработало, просто отображается тот же масштаб, что и раньше. Я, должно быть, делаю что-то не так.
system

17

Вы также можете масштабировать, используя MKCoordinateRegion и задав для него широту и дельту долготы. Ниже приведен краткий справочник, а вот справочник по iOS. Он не сделает ничего необычного, но должен позволить вам установить масштаб при рисовании карты.


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

Изменить 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
Для меня это не имело значения, когда я меняю некоторые значения, карта просто не загружается.
system

Вы устанавливаете это при загрузке карты или пытаетесь манипулировать после загрузки? Используете ли вы в качестве дельт 1 или меньшее число? Просто пытаюсь понять требования.
DerekH

Я ставил перед запуском. Я проверил значения выше и ниже 1.
system

1
Хороший ответ, но попробуйте изменить широту, дельту долготы на 0,1 - увеличено больше.
Daniel Krzyczkowski

12

Простая реализация Swift, если вы используете розетки.

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

На основе ответа @Carnal.


12

Быстрая реализация

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
Я не уверен на 100%, но полагаю, что когда вы создаете свой IBOutletдля своего map, вы определяете его как MapViewWithZoomвместо простого MKMapView. Затем вы можете просто установить уровень масштабирования с помощью map.zoomLevel = 1илиmap.zoomLevel = 0.5
Zonker.in.Geneva

1
некоторые комментарии по этому поводу были бы более полезными. Он работает в Swift 3.
nyxee

Отличное решение! Но мне это больше нравится как расширение, и есть одна странная вещь: чтобы действительно уменьшить масштаб, нужно уменьшить на 2, какmapView.zoomLevel -= 2
Александр

7

Для Swift 3 это довольно быстро:

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

Просто определите <CLLocationDistance>широту и долготу в метрах, и mapView подберет уровень масштабирования для ваших значений.


Что вы имеете в виду под «mapView будет соответствовать уровню масштабирования в соответствии с вашими значениями»? Я предполагаю, что ОП хочет сам установить уровень масштабирования или как бы вы это сделали, учитывая предложенные вами данные?
riper

6

На основе отличного ответа @ AdilSoomro . Я придумал это:

@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

Надеюсь, следующий фрагмент кода вам поможет.

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

Вы можете выбрать любое значение вместо 0,5, чтобы уменьшить или увеличить уровень масштабирования. Я использовал эти методы при нажатии двух кнопок.


2

Ответ Swift 2.0 с использованием NSUserDefaults для сохранения и восстановления масштаба и положения карты.

Функция сохранения положения карты и масштабирования:

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

Запускать функцию каждый раз при перемещении карты:

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

Функция сохранения масштаба и положения карты:

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

Добавьте это в viewDidLoad:

restoreMapRegion()

1

Я знаю, что это запоздалый ответ, но я просто хотел сам решить проблему установки уровня масштабирования. Ответ goldmine отличный, но я обнаружил, что он недостаточно хорошо работает в моем приложении.

При ближайшем рассмотрении в Goldmine говорится, что «линии долготы одинаково разнесены в любой точке карты». Это неправда, на самом деле линии широты расположены одинаково от -90 (южный полюс) до +90 (северный полюс). Наибольшее расстояние между линиями долготы проходит на экваторе и сходится к точке на полюсах.

Поэтому принятая мной реализация заключается в использовании вычисления широты следующим образом:

@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

Надеюсь, это поможет на этом позднем этапе.


Хорошо, игнорируйте вышеизложенное. Goldmine верен, линии долготы РАСПРЕДЕЛЕННЫ на одинаковом расстоянии, потому что, конечно, для карт используется проекция Меркатора. Мои проблемы с решением возникли из-за другой незначительной ошибки в моем приложении, связанной с подклассом нового класса MKTileOverlay iOS 7.
gektron

Возможно, вы захотите обновить свое сообщение, чтобы оно отражало информацию, которую вы включили в свой комментарий.
Дерек Ли

1

Swift:

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

locValue - ваша координата.


1

Здесь я помещаю свой ответ и его работу для быстрой версии 4.2 .

MKMapView center и увеличить


Если я щелкну здесь, прокручу вниз и щелкну там по вашей ссылке, я снова окажусь здесь, а затем я щелкну здесь, и теперь я застрял в бесконечном цикле 😏
Matthijs

@Matthijs Я исправил ссылку. Пожалуйста, проверьте и проголосуйте за ответ.
Ашу

0

На основе ответа Квентинадама

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

Спасибо, хорошо смотрится, когда карта смотрит на север. Но что, если вы вращаете карту? Что делать, если угол защемления отличается от 0?
pierre23,

0

MKMapViewрасширение на основе этого ответа (+ точность уровня масштабирования с плавающей запятой):

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)
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.