определите, был ли MKMapView перетащен/перемещен
есть ли способ определить, перетаскивался ли MKMapView?
Я хочу, чтобы получить расположение центра каждый раз, когда пользователь перетаскивает карту с помощью CLLocationCoordinate2D centre = [locationMap centerCoordinate];
но мне нужен метод делегата или что-то, что срабатывает, как только пользователь переходит с карты.
спасибо заранее
14 ответов:
посмотреть MKMapViewDelegate ссылка.
в частности, эти методы могут быть полезны:
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
убедитесь, что свойство делегата вашего представления карты установлено так, чтобы эти методы вызывались.
код в принятом ответе срабатывает, когда регион изменяется по какой-либо причине. Чтобы правильно определить перетаскивание карты, вам нужно добавить UIPanGestureRecognizer. Кстати, это распознаватель жестов перетаскивания (панорамирование = перетаскивание).
Шаг 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 раз за перетаскивание, обнаружьте, что состояние "drag ended"в селекторе:
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded){ NSLog(@"drag ended"); } }
Это единственный способ, который работал для меня, который обнаруживает пан, а также изменения масштаба, инициированный пользователя:
- (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 } }
(только) 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 } }
Swift 3 решение для Яно выше:
добавьте протокол UIGestureRecognizerDelegate к вашему ViewController
class MyViewController: UIViewController, UIGestureRecognizerDelegate
создайте UIPanGestureRecognizer в
viewDidLoad
и setdelegate
самостоятельно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 } }
по моему опыту, как и "поиск при наборе текста", я нашел таймер-самое надежное решение. Он устраняет необходимость в добавлении дополнительных распознавателей жестов для панорамирования, защемления, поворота, постукивания, двойного постукивания и т. д.
решение простое:
- при изменении области карты установите / сбросьте таймер
когда таймер срабатывает, загрузите маркеры для нового региона
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 } }
вы также можете добавить распознаватель жестов на карту в Interface Builder. Свяжите его с выходом для его действия в вашем viewController, я назвал мой "mapDrag"...
тогда вы будете делать что-то вроде этого в вашем viewController .м:
- (IBAction)mapDrag:(UIPanGestureRecognizer *)sender { if(sender.state == UIGestureRecognizerStateBegan){ NSLog(@"drag started"); } }
убедитесь, что у вас это тоже есть:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; }
конечно, вы будете иметь, чтобы сделать ваш файл ViewController в UIGestureRecognizerDelegate в вашем .H-файл для того, чтобы работать.
в противном случае ответчик карты-единственный, кто слышит событие жеста.
другое возможное решение-реализовать 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; } } }
в некоторых случаях это может быть проще, чем использовать распознаватели жестов.
чтобы распознать, когда какой-либо жест закончился на mapview:
http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/
Это очень полезно только для выполнения запроса к базе данных после того, как пользователь сделал масштабирование/поворот/перетаскивание карты вокруг.
для меня метод regionDidChangeAnimated вызывался только после выполнения жеста и не вызывался много раз, пока перетаскивание / масштабирование / вращение, но полезно знать, было ли это связано с жестом или нет.
многие из этих решений находятся на стороне hacky / not what 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") } }
вам нужно будет обновить класс в ваших файлах раскадровки, чтобы указать на этот подкласс, а также изменить любые карты, которые вы создать с помощью других средств.
ответ Джано работал для меня, поэтому я подумал, что оставлю обновленную версию для 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 заставит этот префикс на вашей функции, чтобы он компилировался.
введите код здесь мне удалось сделать это самым простым способом, который обрабатывает все взаимодействие с картой (нажатие / двойное / N нажатие с 1/2 / N пальцами, панорамирование с 1/2 / N пальцами, щепотка и вращения
- создать
gesture recognizer
и добавить в контейнер вида карты- Set
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 }
Я пытался иметь аннотацию в центре карты, которая всегда находится в центре карты, независимо от того, что делает использование. Я попробовал несколько подходов, упомянутых выше, и ни один из них не был достаточно хорош. В конце концов я нашел очень простой способ решить эту проблему, заимствуя ответ Анны и комбинируя его с ответом Энеко. Он в основном обрабатывает regionWillChangeAnimated как начало перетаскивания, а regionDidChangeAnimated как конец одного, и использует таймер для обновления pin-код в режиме реального времени:
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() }