Определить, было ли перемещено/перемещено MKMapView

Есть ли способ определить, был ли перемещен MKMapView?

Я хочу получить местоположение центра каждый раз, когда пользователь перетаскивает карту с помощью CLLocationCoordinate2D centre = [locationMap centerCoordinate];, но мне нужен метод делегата или что-то, что срабатывает, как только пользователь будет перемещаться по карте.

Заранее спасибо

Ответ 1

Посмотрите на ссылку MKMapViewDelegate.

В частности, эти методы могут быть полезны:

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

Удостоверьтесь, что свойство делегирования вида карты установлено таким образом, чтобы вызываемые методы вызывались.

Ответ 2

Код в принятом ответе запускается, когда область изменяется по какой-либо причине. Чтобы правильно обнаружить перетаскивание карты, вы должны добавить UIPanGestureRecognizer. Btw, это распознаватель жестов (панорамирование = перетаскивание).

Шаг 1: Добавьте распознаватель жестов в viewDidLoad:

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

Шаг 2: Добавьте протокол UIGestureRecognizerDelegate в контроллер представления, чтобы он работал как делегат.

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

Шаг 3: И добавьте следующий код для UIPanGestureRecognizer для работы с уже существующими распознавателями жестов в MKMapView:

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

Шаг 4: Если вы хотите называть свой метод один раз вместо 50 раз за перетаскивание, определите, что в вашем селекторе состояние "перетаскивание":

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

Ответ 3

Это единственный способ, который работал у меня, который обнаруживает панорамирование, а также изменения масштаба, инициированные пользователем:

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

Ответ 4

(Just the) Быстрая версия превосходное решение @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
    }
}

Ответ 5

Решение Swift 3 для ответ Jano выше:

Добавить протокол UIGestureRecognizerDelegate в ваш ViewController

class MyViewController: UIViewController, UIGestureRecognizerDelegate

Создайте 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)
}

Добавьте метод протокола, чтобы ваш распознаватель жестов работал с существующими жестами MKMapView

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

Добавьте метод, который будет вызываться селектором в вашем жесте панорамирования

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

        // do something here

    }
}

Ответ 6

По моему опыту, похожим на "поиск при наборе текста", я обнаружил, что таймер является самым надежным решением. Это устраняет необходимость добавления дополнительных распознавателей жестов для панорамирования, зажимания, поворота, нарезания резьбы, двойного нажатия и т.д.

Решение прост:

  • Когда область карты изменяется, установите / reset таймер
  • Когда срабатывает таймер, маркеры загрузки для новой области

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

Ответ 7

Вы также можете добавить распознаватель жестов к своей карте в Interface Builder. Свяжите его с выходом для своего действия в вашем viewController, я назвал свой "mapDrag"...

Тогда вы сделаете что-то вроде этого в вашем viewController.m:

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

Убедитесь, что у вас есть это тоже:

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

Конечно, вам нужно сделать свой viewController UIGestureRecognizerDelegate в вашем файле .h, чтобы это работало.

В противном случае ответчик карты является единственным, кто слышит событие жестов.

Ответ 8

Еще одно возможное решение - реализовать touchtsMoved: (или 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;
        }
    }
}

Это может быть проще, чем использование распознавателей жестов, в некоторых случаях.

Ответ 9

Чтобы узнать, когда какой-либо жест закончился на карте:

http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/

Это очень полезно для выполнения запроса базы данных только после того, как пользователь выполнил масштабирование/поворот/перемещение карты вокруг.

Для меня метод zoneDidChangeAnimated был вызван только после того, как жест был сделан, и он не вызывался много раз при перетаскивании/масштабировании/вращении, но полезно знать, было ли это из-за жестов или нет.

Ответ 10

Многие из этих решений находятся на взломанной/не той стороне Swift, поэтому я выбрал более чистое решение.

Я просто подклассу MKMapView и переопределяю touchsMoved. Хотя этот фрагмент не включает его, я бы рекомендовал создать делегат или уведомление для передачи любой информации, которая вам нужна относительно движения.

import MapKit

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

        print("Something moved")
    }
}

Вам нужно будет обновить класс в файлах раскадровки, чтобы указать на этот подкласс, а также изменить любые карты, созданные другими средствами.

Ответ 11

Ответ Яно сработал для меня, поэтому я решил оставить обновленную версию для Swift 4/XCode 9, так как я не очень хорошо разбираюсь в Objective C и уверен, что есть и другие, которых нет.

Шаг 1: Добавьте этот код в viewDidLoad:

let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didDragMap(_:)))
panGesture.delegate = self

Шаг 2. Убедитесь, что ваш класс соответствует UIGestureRecognizerDelegate:

class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UIGestureRecognizerDelegate {

Шаг 3: Добавьте следующую функцию, чтобы ваш panGesture работал одновременно с другими жестами:

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

Шаг 4. И убедитесь, что ваш метод не называется "50 раз за перетаскивание", как справедливо отмечает Джано:

@objc func didDragMap(_ gestureRecognizer: UIPanGestureRecognizer) {
    if (gestureRecognizer.state == UIGestureRecognizerState.ended) {
        redoSearchButton.isHidden = false
        resetLocationButton.isHidden = false
    }
}

* Обратите внимание на добавление @objc на последнем шаге. XCode принудительно использует этот префикс для вашей функции для его компиляции.

Ответ 12

Вы можете проверить анимированное свойство, если false, то пользователь перетащил карту

 func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    if animated == false {
        //user dragged map
    }
}

Ответ 13

введите код здесь Мне удалось реализовать это самым простым способом, который обрабатывает все взаимодействие с картой (нажатие/двойное/N нарезание пальцами с 1/2/N пальцами, панорамирование с 1/2/N пальцами, зажим и вращение

  • Создайте gesture recognizer и добавьте в контейнер отображения
  • Задайте gesture recognizer's delegate для некоторого объекта, реализующего UIGestureRecognizerDelegate
  • Внедрить метод gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch)
private func setupGestureRecognizers()
{
    let gestureRecognizer = UITapGestureRecognizer(target: nil, action: nil)
    gestureRecognizer.delegate = self
    self.addGestureRecognizer(gestureRecognizer)
}   

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    self.delegate?.mapCollectionViewBackgroundTouched(self)
    return false
}

Ответ 14

Я пытался создать аннотацию в центре карты, которая всегда находится в центре карты, независимо от того, что делает использование. Я попробовал несколько подходов, упомянутых выше, и ни один из них не был достаточно хорош. В конце концов я нашел очень простой способ решить это, заимствуя у Анны ответ и объединившись с ответом Энеко. Он в основном рассматривает регионWillChangeAnimated как начало перетаскивания, а regionDidChangeAnimated как конец одного и использует таймер для обновления вывода в режиме реального времени:

var mapRegionTimer: Timer?
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
    mapRegionTimer?.invalidate()
    mapRegionTimer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { (t) in
        self.myAnnotation.coordinate = CLLocationCoordinate2DMake(mapView.centerCoordinate.latitude, mapView.centerCoordinate.longitude);
        self.myAnnotation.title = "Current location"
        self.mapView.addAnnotation(self.myAnnotation)
    })
}
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
    mapRegionTimer?.invalidate()
}

Ответ 15

Во-первых, убедитесь, что ваш текущий контроллер представления является делегатом карты. Поэтому установите свой делегат Представления карты на себя и добавьте MKMapViewDelegate в свой контроллер представления. Пример ниже.

class Location_Popup_ViewController: UIViewController, MKMapViewDelegate {
   // Your view controller stuff
}

И добавьте это к вашему виду карты

var myMapView: MKMapView = MKMapView()
myMapView.delegate = self

Во-вторых, добавьте эту функцию, которая запускается при перемещении карты. Он отфильтрует любые анимации и будет срабатывать только при взаимодействии с ним.

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
   if !animated {
       // User must have dragged this, filters out all animations
       // PUT YOUR CODE HERE
   }
}