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中以本机方式执行此操作
可以显式设置边界以限制平移。
请看演示
还可以限制缩放级别
工具书类
- 关于“约束地图视图”
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)
}