Czy istnieje sposób określenia, czy MKMapView został przeciągnięty?
Chcę uzyskać lokalizację centrum za każdym razem, gdy użytkownik przeciąga mapę, CLLocationCoordinate2D centre = [locationMap centerCoordinate];
ale potrzebowałbym metody delegata lub czegoś, co uruchamia się, gdy tylko użytkownik nawiguje po mapie.
Z góry dziękuję
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
wykonał pracę.Kod w zaakceptowanej odpowiedzi odpala, gdy region zostanie zmieniony z jakiegokolwiek powodu. Aby poprawnie wykryć przeciąganie mapy, musisz dodać UIPanGestureRecognizer. Przy okazji, to jest rozpoznawanie gestów przeciągania (przesuwanie = przeciąganie).
Krok 1: Dodaj aparat rozpoznawania gestów w widoku ViewDidLoad:
-(void) viewDidLoad { [super viewDidLoad]; UIPanGestureRecognizer* panRec = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didDragMap:)]; [panRec setDelegate:self]; [self.mapView addGestureRecognizer:panRec]; }
Krok 2: Dodaj protokół UIGestureRecognizerDelegate do kontrolera widoku, aby działał jako delegat.
@interface MapVC : UIViewController <UIGestureRecognizerDelegate, ...>
Krok 3: I dodaj następujący kod dla UIPanGestureRecognizer do pracy z już istniejącymi aparatami rozpoznawczymi gestów w MKMapView:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; }
Krok 4: jeśli chcesz wywołać swoją metodę raz zamiast 50 razy na przeciągnięcie, wykryj w selektorze stan „zakończone przeciąganie”:
- (void)didDragMap:(UIGestureRecognizer*)gestureRecognizer { if (gestureRecognizer.state == UIGestureRecognizerStateEnded){ NSLog(@"drag ended"); } }
źródło
UIPinchGestureRecognizer
takżereadyForUpdate
, a następnie sprawdzam tę flagę- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
.To jedyny sposób, który działał dla mnie, który wykrywa zmiany panoramy i powiększenia zainicjowane przez użytkownika:
- (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 } }
źródło
MKMapView
w iOS. Ta implementacja może się zmienić w każdej aktualizacji iOS, ponieważ nie jest częścią interfejsu API.(Tylko) Szybka wersja doskonałego rozwiązania @ 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 } }
źródło
self.mapView.subviews[0]
naself.mapView.subviews[0] as! UIView
self.map.delegate = self
doSzybkie 3 rozwiązanie powyższej odpowiedzi Jano :
Dodaj protokół UIGestureRecognizerDelegate do swojego ViewController
class MyViewController: UIViewController, UIGestureRecognizerDelegate
Utwórz UIPanGestureRecognizer w programie
viewDidLoad
i ustaw godelegate
na siebieviewDidLoad() { // 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) }
Dodaj metodę protokołu, aby aparat rozpoznawania gestów działał z istniejącymi gestami MKMapView
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }
Dodaj metodę, która zostanie wywołana przez selektor w geście przesuwania
func didDragMap(_ sender: UIGestureRecognizer) { if sender.state == .ended { // do something here } }
źródło
Z mojego doświadczenia wynika, że podobnie jak w przypadku „wyszukiwania podczas pisania”, najbardziej niezawodnym rozwiązaniem jest minutnik. Eliminuje potrzebę dodawania dodatkowych rozpoznawania gestów do przesuwania, szczypania, obracania, stukania, podwójnego stukania itp.
Rozwiązanie jest proste:
Gdy licznik czasu zostanie uruchomiony, znaczniki wczytywania dla nowego regionu
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 } }
źródło
Innym możliwym rozwiązaniem jest zaimplementowanie dotknięćMoved: (lub touchesEnded: itp.) W kontrolerze widoku, który przechowuje widok mapy, na przykład:
-(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; } } }
W niektórych przypadkach może to być prostsze niż używanie funkcji rozpoznawania gestów.
źródło
Możesz także dodać rozpoznawanie gestów do swojej mapy w programie Interface Builder. Połącz go z gniazdem, aby wykonać jego działanie w Twoim kontrolerze viewController, nazwałem go „mapDrag” ...
Następnie zrobisz coś takiego w pliku m ViewController:
- (IBAction)mapDrag:(UIPanGestureRecognizer *)sender { if(sender.state == UIGestureRecognizerStateBegan){ NSLog(@"drag started"); } }
Upewnij się, że masz to też:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; }
Oczywiście będziesz musiał ustawić viewController jako UIGestureRecognizerDelegate w swoim pliku .h, aby to zadziałało.
W przeciwnym razie osoba odpowiadająca na mapie jest jedyną osobą, która słyszy zdarzenie gestu.
źródło
UIGestureRecognizerStateBegan
Aby rozpoznać zakończenie dowolnego gestu w widoku mapy:
[ https://web.archive.org/web/20150215221143/http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/ )
Jest to bardzo przydatne tylko do wykonywania zapytań do bazy danych po zakończeniu przez użytkownika powiększania / obracania / przeciągania mapy.
Dla mnie metoda regionDidChangeAnimated została wywołana dopiero po wykonaniu gestu i nie została wywołana wiele razy podczas przeciągania / powiększania / obracania, ale warto wiedzieć, czy było to spowodowane gestem, czy nie.
źródło
Wiele z tych rozwiązań jest po stronie hacky / nie tego, co zamierzał Swift, więc zdecydowałem się na bardziej przejrzyste rozwiązanie.
Po prostu podklasuję MKMapView i nadpisuję dotknięciaMoved. Chociaż ten fragment go nie zawiera, zalecałbym utworzenie delegata lub powiadomienia, aby przekazać dowolne informacje dotyczące ruchu.
import MapKit class MapView: MKMapView { override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) print("Something moved") } }
Będziesz musiał zaktualizować klasę w swoich plikach scenorysu, aby wskazywała na tę podklasę, a także zmodyfikować wszelkie mapy utworzone w inny sposób.
Jak zauważono w komentarzach, Apple odradza stosowanie podklas
MKMapView
. Chociaż zależy to od uznania programisty, to szczególne użycie nie zmienia zachowania mapy i działa dla mnie bez incydentów przez ponad trzy lata. Jednak wcześniejsze wyniki nie wskazują na przyszłą kompatybilność, więc należy unikać pustki .źródło
Odpowiedź Jano zadziałała dla mnie, więc pomyślałem, że zostawię zaktualizowaną wersję dla Swift 4 / XCode 9, ponieważ nie jestem szczególnie biegły w Celu C i jestem pewien, że jest kilka innych, które też nie są.
Krok 1: Dodaj ten kod w viewDidLoad:
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(didDragMap(_:))) panGesture.delegate = self
Krok 2. Upewnij się, że Twoja klasa jest zgodna z UIGestureRecognizerDelegate:
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UIGestureRecognizerDelegate {
Krok 3: Dodaj następującą funkcję, aby mieć pewność, że panGesture będzie działać jednocześnie z innymi gestami:
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }
Krok 4: I upewnij się, że Twoja metoda nie nazywa się „50 razy na przeciągnięcie”, jak słusznie zauważa Jano:
@objc func didDragMap(_ gestureRecognizer: UIPanGestureRecognizer) { if (gestureRecognizer.state == UIGestureRecognizerState.ended) { redoSearchButton.isHidden = false resetLocationButton.isHidden = false } }
* Zwróć uwagę na dodanie @objc w ostatnim kroku. XCode wymusi ten prefiks w twojej funkcji w celu jej kompilacji.
źródło
Możesz sprawdzić animowaną właściwość, jeśli ma wartość false, a następnie mapę przeciągniętą przez użytkownika
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { if animated == false { //user dragged map } }
źródło
Wiem, że to stary post, ale tutaj mój kod Swift 4/5 odpowiedzi Jano z gestami Pan i 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 } } }
Cieszyć się!
źródło
Starałem się mieć adnotację na środku mapy, która zawsze znajduje się na środku mapy, niezależnie od tego, co robi. Wypróbowałem kilka podejść wymienionych powyżej i żadne z nich nie było wystarczająco dobre. W końcu znalazłem bardzo prosty sposób rozwiązania tego problemu, zapożyczając z odpowiedzi Anny i łącząc z odpowiedzią Eneko. Zasadniczo traktuje regionWillChangeAnimated jako początek przeciągania, a regionDidChangeAnimated jako koniec jednego i używa timera do aktualizowania pinezki w czasie rzeczywistym:
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() }
źródło
tu wpisz kod Udało mi się to zaimplementować w najprostszy sposób, który obsługuje całą interakcję z mapą (stukanie / podwójne / N stukanie palcami 1/2 / N, przesuwanie palcami 1/2 / N, szczypanie i obroty
gesture recognizer
i dodaj do kontenera widoku mapygesture recognizer's
delegate
na implementację jakiegoś obiektuUIGestureRecognizerDelegate
gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch)
metodęźródło
Najpierw upewnij się, że Twój bieżący kontroler widoku jest delegatem mapy. Ustaw więc delegata Map View na siebie i dodaj
MKMapViewDelegate
do kontrolera widoku. Przykład poniżej.class Location_Popup_ViewController: UIViewController, MKMapViewDelegate { // Your view controller stuff }
I dodaj to do widoku mapy
var myMapView: MKMapView = MKMapView() myMapView.delegate = self
Po drugie , dodaj tę funkcję, która jest uruchamiana, gdy mapa jest przesuwana. Odfiltruje wszelkie animacje i uruchomi się tylko w przypadku interakcji.
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { if !animated { // User must have dragged this, filters out all animations // PUT YOUR CODE HERE } }
źródło