Đặt mức thu phóng cho MKMapView


118

Tôi có một bản đồ hiển thị chính xác, điều duy nhất tôi muốn làm bây giờ là đặt mức thu phóng khi nó tải. Có cách nào để làm việc này không?

Cảm ơn

Câu trả lời:


198

Tôi đã tìm thấy cho mình một giải pháp, rất đơn giản và thực hiện được các mẹo nhỏ. Sử dụng MKCoordinateRegionMakeWithDistanceđể đặt khoảng cách theo mét theo chiều dọc và chiều ngang để có được mức thu phóng mong muốn. Và tất nhiên khi bạn cập nhật vị trí của mình, bạn sẽ nhận được tọa độ phù hợp hoặc bạn có thể chỉ định trực tiếp nó trong CLLocationCoordinate2Dlúc khởi động, nếu đó là những gì bạn cần làm:

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

Nhanh:

let location = ...
let region = MKCoordinateRegion( center: location.coordinate, latitudinalMeters: CLLocationDistance(exactly: 5000)!, longitudinalMeters: CLLocationDistance(exactly: 5000)!)
mapView.setRegion(mapView.regionThatFits(region), animated: true)

3
Đây phải là câu trả lời được chọn. Tôi đã thử rất nhiều giải pháp được đề xuất khác nhưng không có giải pháp nào hoạt động đúng cách. Mã này đơn giản và hiệu quả.
Levi Roberts

1
Câu trả lời rất hay. Tuy nhiên, độ phóng đại sẽ khác nhau tùy thuộc vào kích thước màn hình, phải không?
Vinzius

1
Điều thú vị MKCoordinateRegionMakeWithDistancelà vẫn còn tồn tại trong Swift. Giải pháp này hoạt động!
LinusGeffarth

47

Dựa trên thực tế là các đường kinh độ được đặt cách nhau như nhau tại bất kỳ điểm nào trên bản đồ, có một cách triển khai rất đơn giản để đặt centerCogene và 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

Chỉnh sửa nhỏ:- (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

Cảm ơn! Vâng, bạn nói đúng, tôi thực sự đã lấy mã ra khỏi dự án của mình, nơi nó là một chức năng chứ không phải là một bổ sung cho MKMapView. Tôi vừa chỉnh sửa thành mã để phản ánh sự sửa chữa của bạn.
quentinadam

1
Ngược lại của công thức đó là gì, để tìm ra mức thu phóng hiện tại?
Nick

1
Tôi nghĩ nó là thế này:double z = log2(360 * ((self.mapView.frame.size.width/256) / self.mapView.region.span.longitudeDelta));
Nick

1
@devios, ở mức thu phóng 1, cả thế giới (360 °) nằm gọn trong 1 ô rộng 256px. Ở mức thu phóng 2, toàn bộ thế giới (360 °) nằm gọn trong 2 ô 256px (512px). Ở cấp độ zoom 3, cả thế giới (360 °) phù hợp trong 4 gạch của 256px (1024px) vv
quentinadam

31

Nó không được tích hợp sẵn, nhưng tôi đã thấy / sử dụng mã này . Điều này cho phép bạn sử dụng:

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

Lưu ý: Đây không phải là mã của tôi, tôi đã không viết nó, vì vậy không thể ghi nhận nó


1
wow, nó có rất nhiều mã, bạn sẽ nghĩ rằng nó nên được tích hợp sẵn. cảm ơn. sẽ có một cái nhìn như thế nào nó được thực hiện.
hệ thống

1
Bạn có thể lấy tệp .m và .h, thêm nó vào dự án của bạn, sau đó tham chiếu nó trong bộ điều khiển chế độ xem bản đồ của bạn và sử dụng nó như thể đó là một phương pháp trên MKMapView, ôi thật vui vì các danh mục!
Đăng

2
Không hoạt động đối với tôi, nó chỉ hiển thị cùng mức thu phóng như trước đây. Tôi phải làm gì đó sai.
hệ thống

17

Bạn cũng có thể thu phóng bằng cách sử dụng MKCoosystemRegion và thiết lập vĩ độ & kinh độ đồng bằng của nó. Dưới đây là tham khảo nhanh và đây là tham chiếu iOS. Nó sẽ không làm bất cứ điều gì lạ mắt nhưng sẽ cho phép bạn đặt thu phóng khi nó vẽ bản đồ.


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

Chỉnh sửa 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
Điều này không có gì khác biệt đối với tôi, khi tôi thay đổi một số giá trị, nó chỉ không tải bản đồ.
hệ thống

Bạn đang cài đặt điều này khi tải bản đồ hay bạn đang cố gắng thao tác sau khi quá trình tải diễn ra? Bạn có đang sử dụng 1 hoặc một số nhỏ hơn làm delta của mình không? Chỉ cần cố gắng hiểu các yêu cầu.
DerekH

Tôi đặt nó trước thời gian chạy. Tôi đã kiểm tra các giá trị trên và dưới 1.
system

1
Câu trả lời hay nhưng hãy thử thay đổi vĩ độ, kinh độ delta thành 0,1 - nó được phóng to hơn.
Daniel Krzyczkowski

12

Một triển khai Swift đơn giản, nếu bạn sử dụng cửa hàng.

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

Dựa trên câu trả lời của @ Carnal.


12

Triển khai nhanh chóng

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
Tôi không chắc chắn 100%, nhưng tôi đoán rằng khi bạn tạo của bạn IBOutletcho của bạn map, bạn xác định nó là một MapViewWithZoomthay vì đơn giản MKMapView. Sau đó, bạn chỉ có thể thiết lập mức độ zoom với map.zoomLevel = 1hoặcmap.zoomLevel = 0.5
Zonker.in.Geneva

1
một số ý kiến về vấn đề này sẽ hữu ích hơn Nó đang làm việc tại Swift 3.
nyxee

Giải pháp tuyệt vời! Nhưng tôi thích nó hơn như một Tiện ích mở rộng, và có một điều kỳ lạ: để thực sự thu nhỏ, cần phải giảm đi 2 nhưmapView.zoomLevel -= 2
Alexander

7

Đối với Swift 3, nó chuyển tiếp khá nhanh:

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

Chỉ cần xác định vĩ độ, Mét dài <CLLocationDistance>và mapView sẽ điều chỉnh mức thu phóng với giá trị của bạn.


Ý bạn là gì khi "mapView sẽ điều chỉnh mức thu phóng với giá trị của bạn"? Tôi giả sử OP muốn tự đặt mức thu phóng hoặc bạn sẽ làm như thế nào với đầu vào mà bạn đề xuất?
riper

6

Dựa trên câu trả lời tuyệt vời của @ AdilSoomro . Tôi đã nghĩ ra điều này:

@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

Tôi hy vọng đoạn mã sau đây sẽ giúp bạn.

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

Bạn có thể chọn bất kỳ giá trị nào thay vì 0,5 để giảm hoặc tăng mức thu phóng. Tôi đã sử dụng các phương pháp này khi nhấp vào hai nút.


2

Một câu trả lời Swift 2.0 sử dụng NSUserDefaults để lưu và khôi phục độ thu phóng và vị trí của bản đồ.

Chức năng lưu vị trí bản đồ và thu phóng:

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

Chạy chức năng mỗi khi bản đồ được di chuyển:

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

Chức năng lưu phóng to bản đồ và vị trí:

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

Thêm cái này vào viewDidLoad:

restoreMapRegion()

1

Tôi biết đây là một câu trả lời muộn, nhưng tôi chỉ muốn tự mình giải quyết vấn đề đặt mức thu phóng. Câu trả lời của goldmine rất tuyệt nhưng tôi thấy nó không hoạt động tốt trong ứng dụng của mình.

Khi kiểm tra kỹ hơn, mỏ vàng nói rằng "các đường kinh độ được đặt cách nhau như nhau tại bất kỳ điểm nào trên bản đồ". Điều này không đúng, thực tế là các đường vĩ độ được cách đều nhau từ -90 (cực nam) đến +90 (cực bắc). Các đường kinh độ được đặt cách nhau rộng nhất ở đường xích đạo, hội tụ về một điểm ở hai cực.

Do đó, cách triển khai tôi đã áp dụng là sử dụng cách tính vĩ độ như sau:

@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

Hy vọng nó sẽ giúp ở giai đoạn cuối này.


Ok bỏ qua những điều trên. Goldmine đúng là các đường kinh độ được cách đều nhau vì tất nhiên phép chiếu Mercator được sử dụng cho bản đồ. Vấn đề của tôi với giải pháp bắt nguồn từ một lỗi nhỏ khác trong ứng dụng của tôi liên quan đến phân lớp phụ lớp MKTileOverlay của iOS 7 mới.
gektron

Bạn có thể muốn xem xét cập nhật bài đăng của mình để phản ánh thông tin bạn đã đưa vào nhận xét của mình.
Derek Lee

1

Nhanh:

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

locValue là tọa độ của bạn.


1

Ở đây, tôi đã đặt câu trả lời của mình và nó hoạt động cho nhanh chóng 4.2 .

MKMapView trung tâm và phóng to


Nếu tôi nhấp vào đây và cuộn xuống, và nhấp vào liên kết của bạn ở đó, tôi sẽ thấy mình ở đây một lần nữa và sau đó tôi nhấp vào đây và bây giờ tôi bị mắc kẹt trong một vòng lặp vô hạn 😏
Matthijs

@Matthijs Tôi đã sửa lại liên kết. Vui lòng kiểm tra và bình chọn câu trả lời.
Ashu

0

Dựa trên câu trả lời của 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))

Cảm ơn bạn, có vẻ tốt khi bản đồ hướng về phía Bắc. Nhưng nếu bạn đang xoay bản đồ thì sao? Nếu góc chụm khác 0 thì sao?
Pierre23

0

MKMapViewphần mở rộng dựa trên câu trả lời này (+ độ chính xác của mức thu phóng dấu phẩy động):

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)
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.