确定是否拖动/移动了MKMapView

hgb*_*erd 78 mkmapview ios

有没有办法确定是否拖动了MKMapView?

我想在每次用户拖动地图时获取中心位置,CLLocationCoordinate2D centre = [locationMap centerCoordinate];但是我需要一个委托方法或者一旦用户使用地图导航就会触发.

提前致谢

Jan*_*ano 232

当区域因任何原因发生变化时,接受的答案中的代码将触发.要正确检测地图拖动,您必须添加UIPanGestureRecognizer.顺便说一句,这是拖动手势识别器(平移=拖动).

第1步:在viewDidLoad中添加手势识别器:

-(void) viewDidLoad {
    [super viewDidLoad];
    UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didDragMap:)];
    [panRec setDelegate:self];
    [self.mapView addGestureRecognizer:panRec];
}
Run Code Online (Sandbox Code Playgroud)

步骤2:将协议UIGestureRecognizerDelegate添加到视图控制器,使其作为委托.

@interface MapVC : UIViewController <UIGestureRecognizerDelegate, ...>
Run Code Online (Sandbox Code Playgroud)

第3步:并添加以下代码为UIPanGestureRecognizer与在的MKMapView已有的手势识别工作:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
Run Code Online (Sandbox Code Playgroud)

第4步:如果您想要一次调用您的方法一次而不是每次拖动50次,请检测选择器中的"拖动结束"状态:

- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateEnded){
        NSLog(@"drag ended");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,地图视图滚动带有动量,上面的示例将在手势结束但地图视图停止移动之前立即触发.可能有更好的方法,但我所做的是在手势停止时设置一个标志,`readyForUpdate`,然后检查` - (void)mapView中的标志:(MKMapView*)mapView regionDidChangeAnimated:(BOOL)animated `. (30认同)
  • 请注意,用户可以双击单个或两个手指进行缩放,这将更改区域但不会调用此声像识别器. (14认同)
  • 如果你想抓住捏,你也想要添加一个'UIPinchGestureRecognizer`. (3认同)
  • 为什么这个解决方案在底部?这是最好的!是[@mobi的解决方案](http://stackoverflow.com/a/26002176/92756)更简单,但这个更安全. (2认同)

Sno*_*man 70

这是检测平移以及用户启动的缩放更改的唯一方法:

- (BOOL)mapViewRegionDidChangeFromUserInteraction
{
    UIView *view = self.mapView.subviews.firstObject;
    //  Look through gesture recognizers to determine whether this region change is from user interaction
    for(UIGestureRecognizer *recognizer in view.gestureRecognizers) {
        if(recognizer.state == UIGestureRecognizerStateBegan || recognizer.state == UIGestureRecognizerStateEnded) {
            return YES;
        }
    }

    return NO;
}

static BOOL mapChangedFromUserInteraction = NO;

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    mapChangedFromUserInteraction = [self mapViewRegionDidChangeFromUserInteraction];

    if (mapChangedFromUserInteraction) {
        // user changed map region
    }
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    if (mapChangedFromUserInteraction) {
        // user changed map region
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这对我有用,但需要注意的是,这取决于 iOS 中 `MKMapView` 的内部实现。该实现可能会在任何 iOS 更新中更改,因为它不是 API 的一部分。 (2认同)

小智 27

查看MKMapViewDelegate参考.

具体来说,这些方法可能有用:

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
Run Code Online (Sandbox Code Playgroud)

确保设置了地图视图的委托属性,以便调用这些方法.

  • -1因为此解决方案不会告诉您用户是否拖动了地图.如果用户旋转设备或其他方法缩放地图,则不会响应拖动,则会发生regionWillChangeAnimated. (153认同)
  • 好的解决方案 当用户更改位置时,完美地在地图上重新加载注释 (2认同)
  • [@mobi的解决方案](http://stackoverflow.com/a/26002176/92756)通过检查mapviews内部手势识别器来检测用户手势(是的,所有这些).太好了! (2认同)

hEA*_*ASH 26

(只是)Swift版@mobi的优秀解决方案:

private var mapChangedFromUserInteraction = false

private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
    let view = self.mapView.subviews[0]
    //  Look through gesture recognizers to determine whether this region change is from user interaction
    if let gestureRecognizers = view.gestureRecognizers {
        for recognizer in gestureRecognizers {
            if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
                return true
            }
        }
    }
    return false
}

func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
    mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
    if (mapChangedFromUserInteraction) {
        // user changed map region
    }
}

func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    if (mapChangedFromUserInteraction) {
        // user changed map region
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 看起来不错,但我不得不将`self.mapView.subviews [0]`更改为`self.mapView.subviews [0]!UIView` (2认同)
  • 这个(和moby的)解决方案并不是那么出色.无法保证Apple会保留mapViews的第一个子视图.也许在将来的某个版本中,mapView的第一个subView将不会是UIView.所以你的代码不是崩溃驱逐剂.尝试将自己的GestureRecognizers添加到MapView. (2认同)

dst*_*t3p 13

Swift 3解决方案对Jano的答案如下:

将Protocol UIGestureRecognizerDelegate添加到ViewController

class MyViewController: UIViewController, UIGestureRecognizerDelegate
Run Code Online (Sandbox Code Playgroud)

创建UIPanGestureRecognizer viewDidLoad并设置delegate为self

viewDidLoad() {
    // add pan gesture to detect when the map moves
    let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didDragMap(_:)))

    // make your class the delegate of the pan gesture
    panGesture.delegate = self

    // add the gesture to the mapView
    mapView.addGestureRecognizer(panGesture)
}
Run Code Online (Sandbox Code Playgroud)

添加协议方法,以便您的手势识别器可以使用现有的MKMapView手势

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}
Run Code Online (Sandbox Code Playgroud)

添加将在平移手势中由选择器调用的方法

func didDragMap(_ sender: UIGestureRecognizer) {
    if sender.state == .ended {

        // do something here

    }
}
Run Code Online (Sandbox Code Playgroud)


Aar*_*ron 7

另一种可能的解决方案是在保存地图视图的视图控制器中实现touchesMoved :(或touchesEnded:等),如下所示:

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    for (UITouch * touch in touches) {
        CGPoint loc = [touch locationInView:self.mapView];
        if ([self.mapView pointInside:loc withEvent:event]) {
            #do whatever you need to do
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,这可能比使用手势识别器更简单.


Ene*_*nso 7

根据我的经验,类似于"打字时搜索",我发现计时器是最可靠的解决方案.它不需要添加额外的手势识别器来进行平移,捏合,旋转,敲击,双击等.

解决方案很简单:

  1. 当地图区域改变时,设置/重置计时器
  2. 当计时器触发时,加载新区域的标记

    import MapKit
    
    class MyViewController: MKMapViewDelegate {
    
        @IBOutlet var mapView: MKMapView!
        var mapRegionTimer: NSTimer?
    
        // MARK: MapView delegate
    
        func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
            setMapRegionTimer()
        }
    
        func setMapRegionTimer() {
            mapRegionTimer?.invalidate()
            // Configure delay as bet fits your application
            mapRegionTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "mapRegionTimerFired:", userInfo: nil, repeats: false)
        }
    
        func mapRegionTimerFired(sender: AnyObject) {
            // Load markers for current region:
            //   mapView.centerCoordinate or mapView.region
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)


Com*_*ast 6

您还可以在Interface Builder中向地图添加手势识别器.将它链接到你的viewController中的动作插座,我称之为"mapDrag"......

然后你会在viewController的.m中做这样的事情:

- (IBAction)mapDrag:(UIPanGestureRecognizer *)sender {
    if(sender.state == UIGestureRecognizerStateBegan){
        NSLog(@"drag started");
    }
}
Run Code Online (Sandbox Code Playgroud)

确保你也有这个:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}
Run Code Online (Sandbox Code Playgroud)

当然,你必须在你的.h文件中使你的viewController成为UIGestureRecognizerDelegate才能使它工作.

否则,地图的响应者是唯一听到手势事件的人.


Cod*_*der 6

这些解决方案中有很多都存在问题,而不是Swift想要的,所以我选择了一个更干净的解决方案。

我只是将MKMapView子类化,并覆盖touchesMoved。尽管此代码段不包含该代码段,但我建议创建一个委托或通知以传递您想要的有关该运动的任何信息。

import MapKit

class MapView: MKMapView {
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)

        print("Something moved")
    }
}
Run Code Online (Sandbox Code Playgroud)

您将需要更新情节提要文件上的类以指向此子类,以及修改通过其他方式创建的任何地图。

  • “这似乎是最好的解决方案,尽管苹果说不要这样做”,这似乎不是最好的解决方案。 (2认同)

Dou*_*oss 5

要识别地图视图上的任何手势何时结束:

[ https://web.archive.org/web/20150215221143/http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/ )

这对于仅在用户完成缩放/旋转/拖动地图后才执行数据库查询非常有用。

对我来说, regionDidChangeAnimated 方法仅在手势完成后才被调用,并且在拖动/缩放/旋转时不会被多次调用,但知道它是否是由手势引起的很有用。


Bos*_*sOz 5

我知道这是一篇旧文章,但这里是我的Jano 答案的 Swift 4/5 代码,带有平移和捏合手势。

class MapViewController: UIViewController, MapViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.didDragMap(_:)))
        let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(self.didPinchMap(_:)))
        panGesture.delegate = self
        pinchGesture.delegate = self
        mapView.addGestureRecognizer(panGesture)
        mapView.addGestureRecognizer(pinchGesture)
    }

}

extension MapViewController: UIGestureRecognizerDelegate {

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

    @objc func didDragMap(_ sender: UIGestureRecognizer) {
        if sender.state == .ended {
            //code here
        }
    }

    @objc func didPinchMap(_ sender: UIGestureRecognizer) {
        if sender.state == .ended {
            //code here
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

享受!