有没有办法来限制的MKMapView最大变焦水平?(Is there way to limit MK

2019-09-01 22:56发布

问题是 - 有没有办法来限制的MKMapView最大缩放级别? 或者是有办法,当用户放大到那里没有地图可用的图像水平,以跟踪?

Answer 1:

您可以使用mapView:regionWillChangeAnimated:委托方法来监听区域变化事件,如果该区域是比你最大的区域更广,将其设置回用最大区域setRegion:animated:以指示您的用户,他们不能缩小那么远。 这里的方法:

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


Answer 2:

如果你正在与iOS工作7+只,有一个新的camera.altitude属性,您可以获取/设置执行缩放级别。 它相当于azdev的解决方案,但没有外部代码是必需的。

在测试中,我还发现,有可能进入一个无限循环,如果你多次试图在细节放大,所以我有一个变种,以防止在下面我的代码。

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


Answer 3:

我只是花了一些时间在这工作的程序,我要建。 以下是我想出了:

  1. 我开始与特洛伊布兰特对脚本此页面是设置地图视图我想一个更好的办法。

  2. 我加入到返回当前缩放级别的方法。

    在的MKMapView + ZoomLevel.h:

     - (double)getZoomLevel; 

    在的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; } 

    这种方法依赖于上面链接代码的一些私有方法。

  3. 我说这在我的MKMapView委托(如@vladimir以上推荐)

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

    这有重新放大的效果,如果用户得到太远了。 您可以使用regionWillChangeAnimated防止地图从“反弹”回去。

    对于上面的循环评论,它看起来像此方法只遍历一次。



Answer 4:

是的,这是可行的。 首先,通过使用延伸的MKMapView 的MKMapView +缩放级别 。

然后,在你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];
    }
}


Answer 5:

下面是代码斯威夫特3重写使用的MKMapView +缩放级别和@ T.Markle答案:

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

在您的视图控制器的实例:

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


Answer 6:

MKMapView了,它里面,一个MKScrollView (私有API),这是一个子类UIScrollView 。 本的代表MKScrollView是它自己mapView

因此,为了控制最大变焦做到以下几点:

创建的子类MKMapView

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

然后,进入滚动子视图,看到了zoomScale属性。 当变焦比数较大,设定最高变焦。



Answer 7:

不要使用regionWillChangeAnimated 。 使用regionDidChangeAnimated

  • 我们还可以使用setRegion(region, animated: true) 。 通常它会冻结MKMapView如果我们使用regionWillChangeAnimated ,但regionDidChangeAnimated它完美

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


Answer 8:

后拉斐尔Petegrosso与扩展的MKMapView一些小的修改的伟大工程。 下面的版本也更加“人性化”,因为它优雅一旦使用者放开画面,是在手感苹果自己有弹性滚动类似的“对齐”回定义缩放级别。

编辑:该解决方案是不是最优的,将打破/损坏地图上看,我发现了一个更好的解决方案在这里: 如何检测的MKMapView内的任何水龙头 。 这使您可以拦截捏等动作。


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

对于一个硬限制,使用:

#import "MyMapView.h"

@implementation MyMapView

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

}

@end


Answer 9:

下面的代码为我工作在概念上很容易使用,因为它都将根据米的距离的区域。 该代码是从张贴的答案得出:// @妮娃王和张贴@ AWAIS-Fayyaz注释使用regionDidChangeAnimated

以下扩展名添加到您的MapViewDelegate

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

然后如下定义MKCoordinateRegion的延伸。

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

对于MKCoordinateRegion上面的代码片段被张贴@格尔德 - 卡斯坦在这个问题上:

反向MKCoordinateRegionMakeWithDistance的功能?



Answer 10:

我碰到的工作就此问题,并创造了一些作品相当好无需设置一个全球性的限制。

的MapView代表,我是杠杆 - mapViewDidFinishRendering - mapViewRegionDidChange

后面我的解决方案的前提是,由于卫星视图渲染,没有数据的区域总是同样的事情。 这种可怕的图像( http://imgur.com/cm4ou5g )如果我们可以舒适地依靠失败的情况下,我们可以用它作为确定WHA用户正在观看的关键。 地图渲染后,我把渲染的地图边界的截图和determing的平均RGB值。 基于关闭该RGB值的,我认为有问题的区域没有数据。 如果是这样的话我弹出地图退了出去到正确渲染的最后一个跨度。

全球唯一的检查我是当它开始查地图,你可以增加或减少该设置根据您的需求。 下面是原始代码,将做到这一点和捐款将组建一个样本项目。 你可以提供任何优化,将不胜感激,并希望它能帮助。

@property (assign, nonatomic) BOOL isMaxed;
@property (assign, nonatomic) MKCoordinateSpan lastDelta;

self.lastDelta = MKCoordinateSpanMake(0.006, 0.006);

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    if (mapView.mapType != MKMapTypeStandard && self.isMaxed) {
            [self checkRegionWithDelta:self.lastDelta.longitudeDelta];
    }
}


- (void)checkRegionWithDelta:(float)delta {
    if (self.mapView.region.span.longitudeDelta < delta) {
        MKCoordinateRegion region = self.mapView.region;
        region.span = self.lastDelta;
        [self.mapView setRegion:region animated:NO];
    } else if (self.mapView.region.span.longitudeDelta > delta) {
        self.isMaxed = NO;
    }
}


- (void)mapViewDidFinishRenderingMap:(MKMapView *)mapView fullyRendered:(BOOL)fullyRendered {
    if (mapView.mapType != MKMapTypeStandard && !self.isMaxed) {
        [self checkToProcess:self.lastDelta.longitudeDelta];
    }
}


- (void)checkToProcess:(float)delta {
    if (self.mapView.region.span.longitudeDelta < delta) {
        UIGraphicsBeginImageContext(self.mapView.bounds.size);
        [self.mapView.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage *mapImage = UIGraphicsGetImageFromCurrentImageContext();
        [self processImage:mapImage];
    }
}


- (void)processImage:(UIImage *)image {
    self.mapColor = [self averageColor:image];
    const CGFloat* colors = CGColorGetComponents( self.mapColor.CGColor );
    [self handleColorCorrection:colors[0]];
}


- (void)handleColorCorrection:(float)redColor {
    if (redColor < 0.29) {
        self.isMaxed = YES;
        [self.mapView setRegion:MKCoordinateRegionMake(self.mapView.centerCoordinate, self.lastDelta) animated:YES];
    } else {
        self.lastDelta = self.mapView.region.span;
    }
}


- (UIColor *)averageColor:(UIImage *)image {
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char rgba[4];
    CGContextRef context = CGBitmapContextCreate(rgba, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), image.CGImage);
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);

    if(rgba[3] > 0) {
        CGFloat alpha = ((CGFloat)rgba[3])/255.0;
        CGFloat multiplier = alpha/255.0;
        return [UIColor colorWithRed:((CGFloat)rgba[0])*multiplier
                               green:((CGFloat)rgba[1])*multiplier
                                blue:((CGFloat)rgba[2])*multiplier
                               alpha:alpha];
    }
    else {
        return [UIColor colorWithRed:((CGFloat)rgba[0])/255.0
                               green:((CGFloat)rgba[1])/255.0
                                blue:((CGFloat)rgba[2])/255.0
                               alpha:((CGFloat)rgba[3])/255.0];
    }
}


文章来源: Is there way to limit MKMapView maximum zoom level?