ios 确定 MKMapView 是否被拖动/移动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5556977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:27:56  来源:igfitidea点击:

determine if MKMapView was dragged/moved

iosmkmapview

提问by hgbnerd

Is there a way to determine if a MKMapView was dragged around?

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

I want to get the center location every time a user drags the map using CLLocationCoordinate2D centre = [locationMap centerCoordinate];but I'd need a delegate method or something that fires as soon as the user navigates around with the map.

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

Thanks in advance

提前致谢

采纳答案by hEADcRASH

Look at the MKMapViewDelegatereference.

查看MKMapViewDelegate参考。

Specifically, these methods may be useful:

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

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated

Make sure your map view's delegate property is set so those methods get called.

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

回答by Jano

The code in the accepted answer fires when the region is changed for any reason. To properly detect a map drag you have to add a UIPanGestureRecognizer. Btw, this is the drag gesture recognizer (panning = dragging).

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

Step 1:Add the gesture recognizer in viewDidLoad:

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

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

Step 2:Add the protocol UIGestureRecognizerDelegate to the view controller so it works as delegate.

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

@interface MapVC : UIViewController <UIGestureRecognizerDelegate, ...>

Step 3:And add the following code for the UIPanGestureRecognizer to work with the already existing gesture recognizers in MKMapView:

第 3 步:并为 UIPanGestureRecognizer 添加以下代码以与 MKMapView 中现有的手势识别器一起工作:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

Step 4:In case you want to call your method once instead 50 times per drag, detect that "drag ended" state in your selector:

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

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

回答by Snowman

This is the only way that worked for me that detects pan as well as zoom changes initiated by user:

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

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

回答by hEADcRASH

(Just the) Swift version of @mobi's excellent solution:

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

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

回答by dst3p

Swift 3 solution to Jano's answerabove:

上面Jano 回答的Swift 3 解决方案:

Add the Protocol UIGestureRecognizerDelegate to your ViewController

将协议 UIGestureRecognizerDelegate 添加到您的 ViewController

class MyViewController: UIViewController, UIGestureRecognizerDelegate

Create the UIPanGestureRecognizer in viewDidLoadand set delegateto self

在其中创建 UIPanGestureRecognizerviewDidLoad并设置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)
}

Add a Protocol method so your gesture recognizer will work with the existing MKMapView gestures

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

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

Add the method that will be called by the selector in your pan gesture

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

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

        // do something here

    }
}

回答by Aaron

Another possible solution is to implement touchesMoved: (or touchesEnded:, etc.) in the view controller that holds your map view, like so:

另一种可能的解决方案是在保存地图视图的视图控制器中实现 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;
        }
    }
}

This might be simpler than using gesture recognizers, in some cases.

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

回答by Eneko Alonso

In my experience, similar to "search while typing", I found a timer is the most reliable solution. It removes the need for adding additional gesture recognizers for panning, pinching, rotating, tapping, double tapping, etc.

根据我的经验,类似于“边打字边搜索”,我发现计时器是最可靠的解决方案。它消除了增加额外的手势识别的淘洗,捏,旋转,敲击,双点击等需要

The solution is simple:

解决方法很简单:

  1. When the map region changes, set/reset the timer
  2. When the timer fires, load markers for the new region

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

回答by CommaToast

You can also add a gesture recognizer to your map in Interface Builder. Link it up to an outlet for its action in your viewController, I called mine "mapDrag"...

您还可以在 Interface Builder 中向地图添加手势识别器。将它链接到一个插座以在您的 viewController 中执行其操作,我称我的为“mapDrag”...

Then you'll do something like this in your viewController's .m:

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

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

Make sure you have this there too:

确保你也有这个:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

Of course you'll have to make your viewController a UIGestureRecognizerDelegate in your .h file in order for that to work.

当然,您必须在 .h 文件中将 viewController 设为 UIGestureRecognizerDelegate 才能使其正常工作。

Otherwise the map's responder is the only one who hears the gesture event.

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

回答by Doug Voss

To recognize when any gesture ended on the mapview:

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

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

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

This is very useful for only performing a database query after the user is done zooming/rotating/dragging the map around.

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

For me, the regionDidChangeAnimated method only was called after the gesture was done, and didn't get called many times while dragging/zooming/rotating, but it's useful to know if it was due to a gesture or not.

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

回答by CodeBender

A lot of these solutions are on the hacky / not what Swift intended side, so I opted for a cleaner solution.

很多这些解决方案都在 hacky / 而不是 Swift 想要的一面,所以我选择了一个更干净的解决方案。

I simply subclass MKMapView and override touchesMoved. While this snippet does not include it, I would recommend creating a delegate or notification to pass on whatever information you want regarding the movement.

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

import MapKit

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

        print("Something moved")
    }
}

You will need to update the class on your storyboard files to point to this subclass, as well as modify any maps you create through other means.

您需要更新故事板文件上的类以指向该子类,并修改您通过其他方式创建的任何地图。

As noted in the comments, Apple discouragesthe use of subclassing MKMapView. While this falls to the discretion of the developer, this particular usage does not modify the behavior of the map & has worked for me without incident for over three years. However, past performance does not indicate future compatibility, so caveat emptor.

正如评论中所指出的,Apple 不鼓励使用 subclassing MKMapView。虽然这由开发人员自行决定,但这种特殊用法不会改变地图的行为,并且已经为我工作了三年多,没有发生任何事故。但是,过去的表现并不表示未来的兼容性,因此请注意 emptor