wake-up-neo.net

Gibt es eine Möglichkeit, die maximale Zoomstufe von MKMapView zu begrenzen?

die Frage ist - gibt es eine Möglichkeit, die maximale Zoomstufe für MKMapView zu begrenzen? Oder gibt es eine Möglichkeit zu verfolgen, wann der Benutzer auf die Ebene zoomt, auf der kein Kartenbild verfügbar ist?

28
Vladimir

Sie können die Delegate-Methode mapView:regionWillChangeAnimated: verwenden, um auf Regionsänderungsereignisse zu warten. Wenn die Region breiter ist als Ihre maximale Region, setzen Sie sie mit setRegion:animated: wieder auf die maximale Region, um Ihren Benutzer darauf hinzuweisen, dass er nicht so weit verkleinern kann. Hier sind die Methoden:

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated
25
nevan king

Wenn Sie nur mit iOS 7+ arbeiten, gibt es eine neue camera.altitude-Eigenschaft, mit der Sie eine Vergrößerungsstufe erzwingen können. Es entspricht der Lösung von azdev, es ist jedoch kein externer Code erforderlich.

Beim Testen entdeckte ich auch, dass es möglich war, eine Endlosschleife einzugeben, wenn Sie wiederholt versucht haben, Details detailliert zu vergrößern. Ich habe also eine Var-Variable, die das in meinem Code unten verhindert.

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    // enforce maximum zoom level
    if (_mapView.camera.altitude < 120.00 && !_modifyingMap) {
        _modifyingMap = YES; // prevents strange infinite loop case

        _mapView.camera.altitude = 120.00;

        _modifyingMap = NO;
    }
}
27
Ted Avery

Ich habe gerade an einer App gearbeitet, die ich gerade baue. Folgendes habe ich mir ausgedacht:

  1. Ich habe mit Troy Brants Skript auf dieser Seite angefangen. Dies ist eine schönere Art, die Kartenansicht einzustellen, denke ich. 

  2. Ich habe eine Methode hinzugefügt, um die aktuelle Zoomstufe zurückzugeben.

    In MKMapView + ZoomLevel.h:

    - (double)getZoomLevel;
    

    In MKMapView + ZoomLevel.m:

    // Return the current map zoomLevel equivalent, just like above but in reverse
    - (double)getZoomLevel{
        MKCoordinateRegion reg=self.region; // the current visible region
        MKCoordinateSpan span=reg.span; // the deltas
        CLLocationCoordinate2D centerCoordinate=reg.center; // the center in degrees
        // Get the left and right most lonitudes
        CLLocationDegrees leftLongitude=(centerCoordinate.longitude-(span.longitudeDelta/2));
        CLLocationDegrees rightLongitude=(centerCoordinate.longitude+(span.longitudeDelta/2));
        CGSize mapSizeInPixels = self.bounds.size; // the size of the display window
    
        // Get the left and right side of the screen in fully zoomed-in pixels
        double leftPixel=[self longitudeToPixelSpaceX:leftLongitude]; 
        double rightPixel=[self longitudeToPixelSpaceX:rightLongitude];
        // The span of the screen width in fully zoomed-in pixels
        double pixelDelta=abs(rightPixel-leftPixel);
    
        // The ratio of the pixels to what we're actually showing
        double zoomScale= mapSizeInPixels.width /pixelDelta;
        // Inverse exponent
        double zoomExponent=log2(zoomScale);
        // Adjust our scale
        double zoomLevel=zoomExponent+20; 
        return zoomLevel;
    }
    

    Diese Methode basiert auf einigen privaten Methoden im oben verlinkten Code.

  3. Ich habe dies zu meinem MKMapView-Delegierten hinzugefügt (wie @vladimir oben empfohlen).

    - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
        NSLog(@"%f",[mapView getZoomLevel]);
        if([mapView getZoomLevel]<10) {
            [mapView setCenterCoordinate:[mapView centerCoordinate] zoomLevel:10 animated:TRUE];
        }
    }
    

    Dies führt zu einem erneuten Zoomen, wenn der Benutzer zu weit herausfällt. Sie können regionWillChangeAnimated verwenden, um zu verhindern, dass die Karte wieder "aufprallt". 

    In Bezug auf die Schleifenkommentare oben sieht es so aus, als würde diese Methode nur einmal wiederholt.

21
T. Markle

Ja, das ist machbar. Erweitern Sie zuerst MKMapView mit MKMapView + ZoomLevel .

Dann implementieren Sie dies in Ihrem MKMapViewDelegate:

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    // Constrain zoom level to 8.
    if( [mapView zoomLevel] < 8 )
    {
        [mapView setCenterCoordinate:mapView.centerCoordinate 
            zoomLevel:8 
            animated:NO];
    }
}
13
azdev

Hier ist der Code in Swift 3 mit MKMapView + ZoomLevel und @ T.Markle Antwort umgeschrieben:

import Foundation
import MapKit

fileprivate let MERCATOR_OFFSET: Double = 268435456
fileprivate let MERCATOR_RADIUS: Double = 85445659.44705395

extension MKMapView {

    func getZoomLevel() -> Double {

        let reg = self.region
        let span = reg.span
        let centerCoordinate = reg.center

        // Get the left and right most lonitudes
        let leftLongitude = centerCoordinate.longitude - (span.longitudeDelta / 2)
        let rightLongitude = centerCoordinate.longitude + (span.longitudeDelta / 2)
        let mapSizeInPixels = self.bounds.size

        // Get the left and right side of the screen in fully zoomed-in pixels
        let leftPixel = self.longitudeToPixelSpaceX(longitude: leftLongitude)
        let rightPixel = self.longitudeToPixelSpaceX(longitude: rightLongitude)
        let pixelDelta = abs(rightPixel - leftPixel)

        let zoomScale = Double(mapSizeInPixels.width) / pixelDelta
        let zoomExponent = log2(zoomScale)
        let zoomLevel = zoomExponent + 20

        return zoomLevel
    }

    func setCenter(coordinate: CLLocationCoordinate2D, zoomLevel: Int, animated: Bool) {

        let zoom = min(zoomLevel, 28)

        let span = self.coordinateSpan(centerCoordinate: coordinate, zoomLevel: zoom)
        let region = MKCoordinateRegion(center: coordinate, span: span)

        self.setRegion(region, animated: true)
    }

    // MARK: - Private func

    private func coordinateSpan(centerCoordinate: CLLocationCoordinate2D, zoomLevel: Int) -> MKCoordinateSpan {

        // Convert center coordiate to pixel space
        let centerPixelX = self.longitudeToPixelSpaceX(longitude: centerCoordinate.longitude)
        let centerPixelY = self.latitudeToPixelSpaceY(latitude: centerCoordinate.latitude)

        // Determine the scale value from the zoom level
        let zoomExponent = 20 - zoomLevel
        let zoomScale = NSDecimalNumber(decimal: pow(2, zoomExponent)).doubleValue

        // Scale the map’s size in pixel space
        let mapSizeInPixels = self.bounds.size
        let scaledMapWidth = Double(mapSizeInPixels.width) * zoomScale
        let scaledMapHeight = Double(mapSizeInPixels.height) * zoomScale

        // Figure out the position of the top-left pixel
        let topLeftPixelX = centerPixelX - (scaledMapWidth / 2)
        let topLeftPixelY = centerPixelY - (scaledMapHeight / 2)

        // Find delta between left and right longitudes
        let minLng: CLLocationDegrees = self.pixelSpaceXToLongitude(pixelX: topLeftPixelX)
        let maxLng: CLLocationDegrees = self.pixelSpaceXToLongitude(pixelX: topLeftPixelX + scaledMapWidth)
        let longitudeDelta: CLLocationDegrees = maxLng - minLng

        // Find delta between top and bottom latitudes
        let minLat: CLLocationDegrees = self.pixelSpaceYToLatitude(pixelY: topLeftPixelY)
        let maxLat: CLLocationDegrees = self.pixelSpaceYToLatitude(pixelY: topLeftPixelY + scaledMapHeight)
        let latitudeDelta: CLLocationDegrees = -1 * (maxLat - minLat)

        return MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)
    }

    private func longitudeToPixelSpaceX(longitude: Double) -> Double {
        return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0)
    }

    private func latitudeToPixelSpaceY(latitude: Double) -> Double {
        if latitude == 90.0 {
            return 0
        } else if latitude == -90.0 {
            return MERCATOR_OFFSET * 2
        } else {
            return round(MERCATOR_OFFSET - MERCATOR_RADIUS * Double(logf((1 + sinf(Float(latitude * M_PI) / 180.0)) / (1 - sinf(Float(latitude * M_PI) / 180.0))) / 2.0))
        }
    }

    private func pixelSpaceXToLongitude(pixelX: Double) -> Double {
        return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI
    }


    private func pixelSpaceYToLatitude(pixelY: Double) -> Double {
        return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI
    }
}

Anwendungsbeispiel in Ihrem View Controller:

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        print("Zoom: \(mapView.getZoomLevel())")
        if mapView.getZoomLevel() > 6 {
            mapView.setCenter(coordinate: mapView.centerCoordinate, zoomLevel: 6, animated: true)
        }
    }
5
Alina Egorova

Der MKMapViewhat in sich einen MKScrollView(private API), das ist eine Unterklasse von UIScrollViewname__. Der Delegierte dieses MKScrollViewist sein eigener mapViewname__.

Um den maximalen Zoom zu steuern, gehen Sie folgendermaßen vor:

Erstellen Sie eine Unterklasse von MKMapViewname__:

MapView.h

#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>

@interface MapView : MKMapView <UIScrollViewDelegate>

@end

MapView.m

#import "MapView.h"

@implementation MapView

-(void)scrollViewDidZoom:(UIScrollView *)scrollView {

    UIScrollView * scroll = [[[[self subviews] objectAtIndex:0] subviews] objectAtIndex:0];

    if (scroll.zoomScale > 0.09) {
        [scroll setZoomScale:0.09 animated:NO];
    }

}

@end

Greifen Sie dann auf die Unteransicht scrollen zu und sehen Sie die Eigenschaft zoomScalename__. Wenn der Zoom größer als eine Zahl ist, stellen Sie Ihren maximalen Zoom ein.

2

Verwenden Sie nicht regionWillChangeAnimated. Verwenden Sie regionDidChangeAnimated

  • wir können auch setRegion(region, animated: true) verwenden. Normalerweise friert es MKMapView ein, wenn wir regionWillChangeAnimated verwenden, aber mit regionDidChangeAnimated funktioniert es einwandfrei

    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
      mapView.checkSpan()
    }
    
    extension MKMapView {
      func zoom() {
        let region = MKCoordinateRegionMakeWithDistance(userLocation.coordinate, 2000, 2000)
        setRegion(region, animated: true)
      }
    
      func checkSpan() {
        let rect = visibleMapRect
        let westMapPoint = MKMapPointMake(MKMapRectGetMinX(rect), MKMapRectGetMidY(rect))
        let eastMapPoint = MKMapPointMake(MKMapRectGetMaxX(rect), MKMapRectGetMidY(rect))
    
        let distanceInMeter = MKMetersBetweenMapPoints(westMapPoint, eastMapPoint)
    
        if distanceInMeter > 2100 {
          zoom()
        }
      }
    }
    
0
onmyway133

Der folgende Code hat sich für mich bewährt und ist konzeptionell einfach zu verwenden, da er die Region anhand einer Entfernung in Metern festlegt. Der Code wird von der Antwort abgeleitet, die von: @ nevan-king und dem Kommentar von @Awais geschrieben wurde -Fayyaz um regionDidChangeAnimated zu verwenden

Fügen Sie Ihrem MapViewDelegate die folgende Erweiterung hinzu

var currentLocation: CLLocationCoordinate2D?

extension MyMapViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        if self.currentLocation != nil, mapView.region.longitudinalMeters > 1000 {
            let initialLocation = CLLocation(latitude: (self.currentLocation?.latitude)!,
                                         longitude: (self.currentLocation?.longitude)!)
            let coordinateRegion = MKCoordinateRegionMakeWithDistance(initialLocation.coordinate,
                                                                  regionRadius, regionRadius)
            mapView.setRegion(coordinateRegion, animated: true)
        }
    }
}

Definieren Sie anschließend eine Erweiterung für MKCoordinateRegion wie folgt. 

extension MKCoordinateRegion {
    /// middle of the south Edge
    var south: CLLocation {
        return CLLocation(latitude: center.latitude - span.latitudeDelta / 2, longitude: center.longitude)
    }
    /// middle of the north Edge
    var north: CLLocation {
        return CLLocation(latitude: center.latitude + span.latitudeDelta / 2, longitude: center.longitude)
    }
    /// middle of the east Edge
    var east: CLLocation {
        return CLLocation(latitude: center.latitude, longitude: center.longitude + span.longitudeDelta / 2)
    }
    /// middle of the west Edge
    var west: CLLocation {
        return CLLocation(latitude: center.latitude, longitude: center.longitude - span.longitudeDelta / 2)
    }
    /// distance between south and north in meters. Reverse function for MKCoordinateRegionMakeWithDistance
    var latitudinalMeters: CLLocationDistance {
        return south.distance(from: north)
    }
    /// distance between east and west in meters. Reverse function for MKCoordinateRegionMakeWithDistance
    var longitudinalMeters: CLLocationDistance {
        return east.distance(from: west)
    }
}

Der obige Ausschnitt für MKCoordinateRegion wurde zu dieser Frage von @ Gerd-Castan gepostet: 

Umkehrfunktion von MKCoordinateRegionMakeWithDistance?

0
user3474985

Der Beitrag von Raphael Petegrosso mit dem erweiterten MKMapView funktioniert mit einigen kleinen Modifikationen hervorragend. Die folgende Version ist auch "benutzerfreundlicher", da sie sich, sobald der Benutzer anmutet, auf die definierte Zoomstufe zurückzieht Lässt den Bildschirm los und fühlt sich ähnlich wie Apples eigenes Bouncy-Scrolling an.

Bearbeiten: Diese Lösung ist nicht optimal und beschädigt/bricht die Kartenansicht. Ich habe hier eine viel bessere Lösung gefunden: Wie erkennt man einen Tap in einem MKMapView . Dadurch können Sie Quetschungen und andere Bewegungen abfangen.


MyMapView.h

#import <MapKit/MapKit.h>


@interface MyMapView : MKMapView <UIScrollViewDelegate>
@end

MyMapView.m

#import "MyMapView.h"

@implementation MyMapView

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
    if (scale > 0.001)
    {
        [scrollView setZoomScale:0.001 animated:YES];
    }
}
@end

Verwenden Sie für ein hartes Limit Folgendes:

#import "MyMapView.h"

@implementation MyMapView

-(void)scrollViewDidZoom:(UIScrollView *)scrollView
{
    if (scrollView.zoomScale > 0.001)
    {
        [scrollView setZoomScale:0.001 animated:NO];
    }

}

@end
0
zeraien