Iphone 限制MKMapView滚动

Iphone 限制MKMapView滚动,iphone,map,scroll,mkmapview,overlay,Iphone,Map,Scroll,Mkmapview,Overlay,我正在尝试将自定义图像作为MKOverlayView添加到MKMapView中-我需要限制用户在覆盖范围之外滚动。是否有任何现有功能可以执行此操作?还有其他建议吗 谢谢,Matt如果您只想在叠加中冻结地图视图,您可以将地图视图的区域设置为叠加的边界,并将滚动启用和缩放启用设置为否 但这不允许用户在覆盖范围内滚动或缩放 没有内置的方法将地图视图限制为覆盖的边界,因此您必须手动执行。首先,确保您的MKOverlay对象实现了boundingMapRect属性。然后可以在regionDidChange

我正在尝试将自定义图像作为
MKOverlayView
添加到
MKMapView
中-我需要限制用户在覆盖范围之外滚动。是否有任何现有功能可以执行此操作?还有其他建议吗


谢谢,Matt

如果您只想在叠加中冻结地图视图,您可以将地图视图的区域设置为叠加的边界,并将
滚动启用
缩放启用
设置为

但这不允许用户在覆盖范围内滚动或缩放

没有内置的方法将地图视图限制为覆盖的边界,因此您必须手动执行。首先,确保您的
MKOverlay
对象实现了
boundingMapRect
属性。然后可以在
regionDidChangeAnimated
delegate方法中使用,根据需要手动调整视图

下面是一个如何做到这一点的示例。
下面的代码应该在具有MKMapView的类中
确保地图视图最初设置为覆盖可见的区域

//add two ivars to the .h...
MKMapRect lastGoodMapRect;
BOOL manuallyChangingMapRect;

//in the .m...
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    if (manuallyChangingMapRect)
        return;     
    lastGoodMapRect = mapView.visibleMapRect;
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    if (manuallyChangingMapRect) //prevents possible infinite recursion when we call setVisibleMapRect below
        return;     

    // "theOverlay" below is a reference to your MKOverlay object.
    // It could be an ivar or obtained from mapView.overlays array.

    BOOL mapContainsOverlay = MKMapRectContainsRect(mapView.visibleMapRect, theOverlay.boundingMapRect);

    if (mapContainsOverlay)
    {
        // The overlay is entirely inside the map view but adjust if user is zoomed out too much...
        double widthRatio = theOverlay.boundingMapRect.size.width / mapView.visibleMapRect.size.width;
        double heightRatio = theOverlay.boundingMapRect.size.height / mapView.visibleMapRect.size.height;
        if ((widthRatio < 0.6) || (heightRatio < 0.6)) //adjust ratios as needed
        {
            manuallyChangingMapRect = YES;
            [mapView setVisibleMapRect:theOverlay.boundingMapRect animated:YES];
            manuallyChangingMapRect = NO;
        }
    }
    else
        if (![theOverlay intersectsMapRect:mapView.visibleMapRect])
        {
            // Overlay is no longer visible in the map view.
            // Reset to last "good" map rect...
            [mapView setVisibleMapRect:lastGoodMapRect animated:YES];
        }   
}
如果有人正在寻找快速的
MKOverlay
解决方案,这里有一个:

-(void)viewDidLoad{
[超级视图下载];
MKCircle*circleOverlay=[MKCircle circleWithMapRect:istanbulRect];
[_MapViewAddOverlay:circleOverlay];
覆盖层=圆形覆盖层;
}
-(MKOverlayView*)地图视图:(MKMapView*)地图视图覆盖:(id)覆盖{
MKCircleView*circleOverlay=[[MKCircleView alloc]initWithCircle:overlay];
[circleOverlay setStrokeColor:[UIColor mainColor]];
[circleOverlay设置线宽:4.f];
返回圆覆盖;
}

在我的例子中,我需要将边界限制为具有左上/右下坐标的平铺覆盖。上面的代码仍然运行良好,但用overlay.boundingMapRect替换了MKMapRect paddedBoundingMapRect

- (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated
{
if (manuallyChangingMapRect) //prevents possible infinite recursion when we call setVisibleMapRect below
    return;     

[self updateDynamicPaddedBounds];

MKMapPoint pt =  MKMapPointForCoordinate( mapView.centerCoordinate);

BOOL mapInsidePaddedBoundingRect = MKMapRectContainsPoint(paddedBoundingMapRect,pt );

if (!mapInsidePaddedBoundingRect)
{
    // Overlay is no longer visible in the map view.
    // Reset to last "good" map rect...

    manuallyChangingMapRect = YES;
    [mapView setVisibleMapRect:lastGoodMapRect animated:YES];
    manuallyChangingMapRect = NO;


}


-(void)updateDynamicPaddedBounds{

ENTER_METHOD;

CLLocationCoordinate2D  northWestPoint= CLLocationCoordinate2DMake(-33.841171,151.237318 );
CLLocationCoordinate2D  southEastPoint= CLLocationCoordinate2DMake(-33.846127, 151.245058);



MKMapPoint upperLeft = MKMapPointForCoordinate(northWestPoint);
MKMapPoint lowerRight = MKMapPointForCoordinate(southEastPoint);
double width = lowerRight.x - upperLeft.x;
double height = lowerRight.y - upperLeft.y;


MKMapRect mRect = mapView.visibleMapRect;
MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect));
MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect));
MKMapPoint northMapPoint = MKMapPointMake(MKMapRectGetMidX(mRect), MKMapRectGetMaxY(mRect));
MKMapPoint southMapPoint = MKMapPointMake(MKMapRectGetMidX(mRect), MKMapRectGetMinY(mRect));

double xMidDist = abs(eastMapPoint.x -  westMapPoint.x)/2;
double yMidDist = abs(northMapPoint.y -  southMapPoint.y)/2;


upperLeft.x = upperLeft.x + xMidDist;
upperLeft.y = upperLeft.y + yMidDist;


double paddedWidth =  width - (xMidDist*2); 
double paddedHeight = height - (yMidDist*2);

paddedBoundingMapRect= MKMapRectMake(upperLeft.x, upperLeft.y, paddedWidth, paddedHeight);
}

在Swift 3.0中,我将Anna的()解决方案添加到一个扩展中:

extension HomeViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    if manuallyChangingMapRect {
        return
    }
    guard let overlay = self.mapOverlay else {
        print("Overlay is nil")
        return
    }
    guard let lastMapRect = self.lastGoodMapRect else {
        print("LastGoodMapRect is nil")
        return
    }

    let mapContainsOverlay = MKMapRectContainsRect(mapView.visibleMapRect, overlay.boundingMapRect)
    if mapContainsOverlay {
        let widthRatio: Double = overlay.boundingMapRect.size.width / mapView.visibleMapRect.size.width
        let heightRatio: Double = overlay.boundingMapRect.size.height / mapView.visibleMapRect.size.height
        // adjust ratios as needed
        if (widthRatio < 0.9) || (heightRatio < 0.9) {
            manuallyChangingMapRect = true
            mapView.setVisibleMapRect(overlay.boundingMapRect, animated: true)
            manuallyChangingMapRect = false
        }
    } else if !overlay.intersects(mapView.visibleMapRect) {
            // Overlay is no longer visible in the map view.
            // Reset to last "good" map rect...
            manuallyChangingMapRect = true
            mapView.setVisibleMapRect(lastMapRect, animated: true)
            manuallyChangingMapRect = false
        }
        else {
            lastGoodMapRect = mapView.visibleMapRect
    }
}
}
Swift 4的好答案

使用以下代码,您可以检测滚动的绑定限制

注:在下面的代码5000中,数字是以米为单位的限制面积。所以您可以这样使用>
let restricedAreaMeters=5000

func detectBoundingBox(location: CLLocation) {
        let latRadian = degreesToRadians(degrees: CGFloat(location.coordinate.latitude))
        let degLatKm = 110.574235
        let degLongKm = 110.572833 * cos(latRadian)
        let deltaLat = 5000 / 1000.0 / degLatKm 
        let deltaLong = 5000 / 1000.0 / degLongKm

        southLimitation = location.coordinate.latitude - deltaLat
        westLimitation = Double(CGFloat(location.coordinate.longitude) - deltaLong)
        northLimitation =  location.coordinate.latitude + deltaLat
        eastLimitation = Double(CGFloat(location.coordinate.longitude) + deltaLong)
    }

    func degreesToRadians(degrees: CGFloat) -> CGFloat {
        return degrees * CGFloat(M_PI) / 180
    }
最后,若用户从有界区域出来,则使用下面的重写方法将返回到最新允许的坐标

 var lastCenterCoordinate: CLLocationCoordinate2D!
 extension UIViewController: MKMapViewDelegate {
        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { 
           let coordinate = CLLocationCoordinate2DMake(mapView.region.center.latitude, mapView.region.center.longitude) 
           let latitude = mapView.region.center.latitude
           let longitude = mapView.region.center.longitude

            if latitude < northLimitation && latitude > southLimitation && longitude < eastLimitation && longitude > westLimitation {
                lastCenterCoordinate = coordinate
            } else {
                span = MKCoordinateSpanMake(0, 360 / pow(2, Double(16)) * Double(mapView.frame.size.width) / 256)
                let region = MKCoordinateRegionMake(lastCenterCoordinate, span)
                mapView.setRegion(region, animated: true)
            }
        }
 }
var lastCenterCoordinate:CLLocationCoordinate2D!
扩展UIViewController:MKMapViewDelegate{
func映射视图(uMapView:MKMapView,RegiondChangeAnimated:Bool){
让坐标=CLLocationCoordinate2DMake(mapView.region.center.latitude,mapView.region.center.longitude)
设纬度=mapView.region.center.latitude
让经度=mapView.region.center.longitude
如果纬度<北限和纬度>南限和经度<东限和经度>西限{
lastCenterCoordinate=坐标
}否则{
span=MKCoordinateSpanMake(0,360/pow(2,Double(16))*Double(mapView.frame.size.width)/256)
让区域=MKCoordinateRegionMake(最后一个中心坐标,跨度)
mapView.setRegion(区域,动画:true)
}
}
}
MapKit现在在iOS 13中以本机方式执行此操作 可以显式设置边界以限制平移。 请看演示

还可以限制缩放级别 工具书类
  • 关于“约束地图视图”
SWIFT 5 在地图视图中使用的简单解决方案DidFinishLoadingMap

    func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {

        //center of USA, roughly. for example
        let center = CLLocationCoordinate2D(latitude: 38.573936, longitude: -92.603760) 
        let latMeters = CLLocationDistance(10_000_000.00) //left and right pan
        let longMeters = CLLocationDistance(5_000_000.00) //up and down pan
        
        let coordinateRegion = MKCoordinateRegion(
            center: center,
            latitudinalMeters: latMeters,
            longitudinalMeters: longMeters)
        
        let cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: coordinateRegion)
        mapView.setCameraBoundary(cameraBoundary, animated: true)
    }

工作相当顺利。不处理平移(滑动、滚动等)超出边界的视图的情况。@horseshoee7,在结束手势后视图是否会恢复到覆盖中?此外,贴图必须从覆盖层内部开始,以使上述操作生效。如果您需要在用户移动地图时进行限制,您可以使用UIPangestureRecognitor并在手势处理程序中检查新的地图区域。@Annakarena您的代码非常棒,特别是因为注释非常好。然而,也有一些情况你错过了,导致代码在不同的位置之间振荡,然后做一个无限循环(如果你问我,有点像特技)。因此,我将添加我的代码,如果您觉得它有用,请将它与您的代码合并。@huggie:我不知道任何有文档记录的方式来获取内部的滚动视图,我也没有探索(也不想依赖)任何无文档记录的方法。另一种可能有效的方法(未尝试过)是使用平移和收缩手势识别器截获贴图区域的更改,该识别器的触发频率高于贴图的委托方法(请参阅)。在你感兴趣的区域之外,也有简单的隐藏或覆盖世界。人们可能更倾向于考虑MKMVIEWVIEW的区域:作为处理纵横比的一种选择:
func detectBoundingBox(location: CLLocation) {
        let latRadian = degreesToRadians(degrees: CGFloat(location.coordinate.latitude))
        let degLatKm = 110.574235
        let degLongKm = 110.572833 * cos(latRadian)
        let deltaLat = 5000 / 1000.0 / degLatKm 
        let deltaLong = 5000 / 1000.0 / degLongKm

        southLimitation = location.coordinate.latitude - deltaLat
        westLimitation = Double(CGFloat(location.coordinate.longitude) - deltaLong)
        northLimitation =  location.coordinate.latitude + deltaLat
        eastLimitation = Double(CGFloat(location.coordinate.longitude) + deltaLong)
    }

    func degreesToRadians(degrees: CGFloat) -> CGFloat {
        return degrees * CGFloat(M_PI) / 180
    }
 var lastCenterCoordinate: CLLocationCoordinate2D!
 extension UIViewController: MKMapViewDelegate {
        func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { 
           let coordinate = CLLocationCoordinate2DMake(mapView.region.center.latitude, mapView.region.center.longitude) 
           let latitude = mapView.region.center.latitude
           let longitude = mapView.region.center.longitude

            if latitude < northLimitation && latitude > southLimitation && longitude < eastLimitation && longitude > westLimitation {
                lastCenterCoordinate = coordinate
            } else {
                span = MKCoordinateSpanMake(0, 360 / pow(2, Double(16)) * Double(mapView.frame.size.width) / 256)
                let region = MKCoordinateRegionMake(lastCenterCoordinate, span)
                mapView.setRegion(region, animated: true)
            }
        }
 }
let boundaryRegion = MKCoordinateRegion(...) // the region you want to restrict
let cameraBoundary = CameraBoundary(region: boundaryRegion)
mapView.setCameraBoundary(cameraBoundary: cameraBoundary, animated: true)
let zoomRange = CameraZoomRange(minCenterCoordinateDistance: 100,
    maxCenterCoordinateDistance: 500)
mapView.setCameraZoomRange(zoomRange, animated: true)
    func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {

        //center of USA, roughly. for example
        let center = CLLocationCoordinate2D(latitude: 38.573936, longitude: -92.603760) 
        let latMeters = CLLocationDistance(10_000_000.00) //left and right pan
        let longMeters = CLLocationDistance(5_000_000.00) //up and down pan
        
        let coordinateRegion = MKCoordinateRegion(
            center: center,
            latitudinalMeters: latMeters,
            longitudinalMeters: longMeters)
        
        let cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: coordinateRegion)
        mapView.setCameraBoundary(cameraBoundary, animated: true)
    }