我正在尝试将自定义图片作为MKMapView
添加到MKOverlayView
- 我需要限制用户无法滚动到叠加层的边界之外。有没有现成的功能呢?还是其他任何建议?
谢谢,马特
答案 0 :(得分:19)
如果您只想冻结叠加层上的地图视图,可以将地图视图的区域设置为叠加层的边界,并将scrollEnabled
和zoomEnabled
设置为NO
。
但这不会让用户在叠加层的边界内滚动或缩放。
没有内置的方法将地图视图限制为叠加层的边界,因此您必须手动执行此操作。首先,确保您的MKOverlay
对象实现boundingMapRect
属性。然后可以在regionDidChangeAnimated
委托方法中使用它来根据需要手动调整视图。
这是一个如何做到这一点的例子
下面的代码应该在具有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];
}
}
我使用内置的MKCircle
叠加层进行了尝试,但似乎运行良好。
修改强>
它在95%的情况下运行良好,但是,我通过一些测试确认它可能在两个位置之间振荡,然后进入无限循环。所以,我编辑了一下,我认为这应该可以解决问题:
// You can safely delete this method:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated {
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
// prevents possible infinite recursion when we call setVisibleMapRect below
if (manuallyChangingMapRect) {
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;
// adjust ratios as needed
if ((widthRatio < 0.6) || (heightRatio < 0.6)) {
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...
manuallyChangingMapRect = YES;
[mapView setVisibleMapRect:lastGoodMapRect animated:YES];
manuallyChangingMapRect = NO;
} else {
lastGoodMapRect = mapView.visibleMapRect;
}
}
如果有人正在寻找快速MKOverlay
解决方案,请参阅以下内容:
- (void)viewDidLoad {
[super viewDidLoad];
MKCircle* circleOverlay = [MKCircle circleWithMapRect:istanbulRect];
[_mapView addOverlay:circleOverlay];
theOverlay = circleOverlay;
}
- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay {
MKCircleView* circleOverlay = [[MKCircleView alloc] initWithCircle:overlay];
[circleOverlay setStrokeColor:[UIColor mainColor]];
[circleOverlay setLineWidth:4.f];
return circleOverlay;
}
答案 1 :(得分:4)
在我的情况下,我需要将边界限制为具有upperleft / lowerRight坐标的平铺覆盖。上面的代码仍然可以很好地工作,但是替换了OverMap.boundingMapRect for 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);
}
答案 2 :(得分:0)
Anna在Swift 3.0中的解决方案(https://stackoverflow.com/a/4126011/3191130),我添加了扩展程序:
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
}
}
}
要设置地图,请使用:
override func viewDidLoad() {
super.viewDidLoad()
setupMap()
}
func setupMap() {
mapView.delegate = self
let radius:CLLocationDistance = 1000000
mapOverlay = MKCircle(center: getCenterCoord(), radius: radius)
if let mapOverlay = mapOverlay {
mapView.add(mapOverlay)
}
mapView.setRegion(MKCoordinateRegionMake(getCenterCoord(), getSpan()), animated: true)
lastGoodMapRect = mapView.visibleMapRect
}
func getCenterCoord() -> CLLocationCoordinate2D {
return CLLocationCoordinate2DMake(LAT, LON)
}
func getSpan() -> MKCoordinateSpan {
return MKCoordinateSpanMake(10, 10)
}
答案 3 :(得分:0)
Swift 4
的好答案
使用以下代码可以检测滚动的界限
注意:在下面的代码5000中,数字是以米为单位的限制区域的数量。所以你可以像这样使用&gt; Dim desk = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim args As String 'declare args
'args = " -i C:\mediapubisert\out%03d.jpg -r 10 C:\mediapubisert\out.mp4 "
args = " -i C:\mediapubisert\out%02d.jpg -c:v libx264 -r 30 -pix_fmt yuv420p C:\mediapubisert\out.mp4 "
Dim proc As New Process
Dim proci As New ProcessStartInfo
proci.FileName = My.Application.Info.DirectoryPath & "\ffmpeg.exe"
proci.Arguments = args
proci.WindowStyle = ProcessWindowStyle.Hidden
proci.CreateNoWindow = True
proci.UseShellExecute = False
proc.StartInfo = proci
proc.Start()
Do Until proc.HasExited = True
Me.Text = "Saving"
Loop
Me.Text = "your video done"
'My.Computer.FileSystem.CopyFile(TextBox1.Text, DestPath + TextBox1.Text)
MsgBox("Done")
Dim directoryName As String = "C:\mediapubisert\"
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
}
答案 4 :(得分:0)
let boundaryRegion = MKCoordinateRegion(...) // the region you want to restrict
let cameraBoundary = CameraBoundary(region: boundaryRegion)
mapView.setCameraBoundary(cameraBoundary: cameraBoundary, animated: true)
请参见WWDC 2019 video at 2378 seconds进行演示。
let zoomRange = CameraZoomRange(minCenterCoordinateDistance: 100,
maxCenterCoordinateDistance: 500)
mapView.setCameraZoomRange(zoomRange, animated: true)
答案 5 :(得分:0)
在 mapViewDidFinishLoadingMap 中使用的简单解决方案:
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)
}