Определить, было ли перемещено/перемещено 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
}
}