Есть ли способ определить, перетаскивали ли MKMapView?
Я хочу получать центральное местоположение каждый раз, когда пользователь перетаскивает карту с помощью, CLLocationCoordinate2D centre = [locationMap centerCoordinate];
но мне нужен метод делегата или что-то, что срабатывает, как только пользователь перемещается по карте.
заранее спасибо
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(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 раз за перетаскивание, определите состояние «перетаскивание завершено» в вашем селекторе:
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded){ NSLog(@"drag ended"); } }
источник
UIPinchGestureRecognizer
а такжеreadyForUpdate
, а затем проверил этот флаг- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
.Это единственный способ, который сработал для меня, который обнаруживает изменения панорамирования и масштабирования, инициированные пользователем:
- (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 } }
источник
MKMapView
в iOS. Эта реализация может измениться в любом обновлении iOS, поскольку она не является частью API.(Просто) 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 } }
источник
self.mapView.subviews[0]
наself.mapView.subviews[0] as! UIView
self.map.delegate = self
сработало, мне пришлось добавить в viewDidLoadРешение Swift 3 для ответа Яно выше:
Добавьте протокол UIGestureRecognizerDelegate в свой ViewController
class MyViewController: UIViewController, UIGestureRecognizerDelegate
Создайте UIPanGestureRecognizer в
viewDidLoad
и установитеdelegate
для себя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 } }
источник
Другое возможное решение - реализовать 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; } } }
В некоторых случаях это может быть проще, чем использование распознавателей жестов.
источник
Вы также можете добавить распознаватель жестов к своей карте в Интерфейсном Разработчике. Свяжите его с выходом для его действия в вашем 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 файле, чтобы это работало.
В противном случае ответчик карты - единственный, кто слышит событие жеста.
источник
UIGestureRecognizerStateBegan
Чтобы распознать, когда какой-либо жест закончился в обзоре карты:
[ https://web.archive.org/web/20150215221143/http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/ )
Это очень полезно для выполнения запроса к базе данных только после того, как пользователь выполнит масштабирование / поворот / перетаскивание карты.
Для меня метод regionDidChangeAnimated вызывался только после выполнения жеста и не вызывался много раз при перетаскивании / масштабировании / повороте, но полезно знать, было ли это связано с жестом или нет.
источник
Многие из этих решений являются хакерскими / не такими, как задумал Swift, поэтому я выбрал более чистое решение.
Я просто создаю подкласс MKMapView и переопределяю touchMoved. Хотя этот фрагмент не включает его, я бы порекомендовал создать делегата или уведомление для передачи любой информации о перемещении, которую вы хотите.
import MapKit class MapView: MKMapView { override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) print("Something moved") } }
Вам нужно будет обновить класс в ваших файлах раскадровки, чтобы указать на этот подкласс, а также изменить любые карты, которые вы создаете с помощью других средств.
Как отмечено в комментариях, Apple не рекомендует использовать подклассы
MKMapView
. Хотя это остается на усмотрение разработчика, это конкретное использование не меняет поведения карты и работает у меня без происшествий более трех лет. Однако прошлые показатели не указывают на совместимость в будущем, поэтому будьте осторожны .источник
Вы можете проверить анимированное свойство, если false, тогда пользователь перетащил карту
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { if animated == false { //user dragged map } }
источник
Ответ Яно сработал для меня, поэтому я подумал, что оставлю обновленную версию для 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 принудительно установит этот префикс в вашей функции, чтобы она скомпилировалась.
источник
Я знаю, что это старый пост, но здесь мой код Swift 4/5 ответа Яно с жестами Pan и Pinch.
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 } } }
Наслаждайтесь!
источник
Я пытался сделать аннотацию в центре карты, которая всегда будет в центре карты, независимо от того, что используется. Я пробовал несколько подходов, упомянутых выше, и ни один из них не был достаточно хорош. В конце концов я нашел очень простой способ решить эту проблему, заимствовав ответ Анны и объединив с ответом Энеко. Он в основном рассматривает regionWillChangeAnimated как начало перетаскивания, а 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() }
источник
введите здесь код, мне удалось реализовать это самым простым способом, который обрабатывает все взаимодействия с картой (нажатие / двойное / N касание пальцами 1/2 / N, панорамирование пальцами 1/2 / N, сжатие и вращения
gesture recognizer
и добавить в контейнер вида картыgesture recognizer's
delegate
для некоторого объекта, реализующегоUIGestureRecognizerDelegate
gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch)
Метод реализацииисточник
Во-первых , убедитесь, что ваш текущий контроллер представления является делегатом карты. Итак, установите своего делегата Map View на себя и добавьте
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 } }
источник