MKMapView가 드래그 / 이동되었는지 확인


84

MKMapView가 드래그되었는지 확인하는 방법이 있습니까?

사용자가 사용하여지도를 끌 때마다 중심 위치를 얻고 싶지만 사용자가지도를 CLLocationCoordinate2D centre = [locationMap centerCoordinate];탐색하자마자 실행되는 델리게이트 메서드 또는 무언가가 필요합니다.

미리 감사드립니다

답변:


22

상기 봐 MKMapViewDelegate의 참조입니다.

특히 다음 방법이 유용 할 수 있습니다.

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

해당 메서드가 호출되도록 맵 뷰의 대리자 속성이 설정되어 있는지 확인합니다.


1
감사합니다. - (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated일을했다.
hgbnerd 2011

2
훌륭한 솔루션입니다. 사용자가 위치를 변경할 때지도에 주석을 다시로드하는 데 완벽 함
Alejandro Luengo

176
-1이 솔루션은 사용자가지도를 드래그했는지 여부를 알려주지 않기 때문입니다. regionWillChangeAnimated는 사용자가 기기를 회전하거나 다른 메소드가지도를 확대 / 축소하는 경우 발생합니다. 반드시 드래그에 응답하지 않아도됩니다.
CommaToast 2014 년

감사합니다 @CommaToast이 '답변'에서 동일한 문제를 발견했습니다
cleverbit

3
@mobi의 솔루션 은 mapviews 내부 제스처 인식기를 확인하여 사용자 제스처 (예, 모두)를 감지합니다. 좋은!
Felix Alcala 2014

236

어떤 이유로 든 지역이 변경되면 수락 된 답변의 코드가 실행됩니다. 지도 끌기를 제대로 감지하려면 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");
    }
}

나는 이것이 상당히 오래된 게시물이라는 것을 알고 있지만 위의 아이디어를 좋아합니다. 내 구현과 함께 regionDidChange 메서드로 내 앱을 구성하는 데 어려움을 겪었으며 이것을 보았을 때 모든 것이 클릭되었고 어떤 이유로 든 regionDidChange가 실행되는 것이 맞습니다. 내가 원하는 것을 정확하게 할 수 있도록지도를 얻을 수 있으므로 이상적이지 않습니다.
Alex McPherson

3
당신이 너무 캐치 핀치하려는 경우를 추가 할 수 있습니다 UIPinchGestureRecognizer뿐만 아니라
그레고리 코스모 혼에게

32
지도보기 스크롤은 추진력을 전달하며, 위의 예는 제스처가 끝나 자마자지도보기가 이동을 중지하기 전에 실행됩니다. 더 나은 방법이있을 수 있지만 제가 한 것은 제스처가 멈출 때 플래그를 설정 한 readyForUpdate다음에서 해당 플래그를 확인하는 것입니다 - (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated.
tvon

14
사용자는 한 손가락 또는 두 손가락을 두 번 탭하여 확대 / 축소 할 수 있습니다. 이렇게하면 영역이 변경되지만이 팬 인식기는 호출되지 않습니다.
SomeGuy 2014-07-02

2
이 솔루션이 맨 아래에있는 이유는 무엇입니까? 최고예요! 예 @mobi의 솔루션 은 더 간단하지만 이것은 더 안전합니다.
Leslie Godwin

77

이것은 사용자가 시작한 확대 / 축소 변경과 이동을 감지하는 유일한 방법입니다.

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

2
이것은 나를 위해 작동하지만 이것은 MKMapViewiOS 의 내부 구현에 따라 다릅니다 . 이 구현은 API의 일부가 아니기 때문에 모든 iOS 업데이트에서 변경 될 수 있습니다.
progrmr

이것은 효과가 있으며 거기에 무엇이 있는지 변경하지 않기 때문에 주요 답변보다 더 좋습니다.
QED

코드 대 사용자지도 조작의 우아한 솔루션에 감사드립니다.
djneely 2011

32

(그냥) @mobi의 탁월한 솔루션의 Swift 버전 :

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

2
외모 좋은,하지만 난 변화했다 self.mapView.subviews[0]self.mapView.subviews[0] as! UIView
kmurph79

3
이 (그리고 moby의) 솔루션은 그렇게 훌륭하지 않습니다. Apple이 mapViews 첫 번째 하위보기를 보존한다는 보장은 없습니다. 향후 버전에서는 mapView의 첫 번째 subView가 UIView가 아닐 것입니다. 따라서 코드는 충돌 방지 기능이 없습니다. 자신 만의 GestureRecognizer를 MapView에 추가해보세요.
Samet DEDE

1
이 작업을 수행 self.map.delegate = self하려면 viewDidLoad
tylerSF

17

의 Jano의 답변에 대한 Swift 3 솔루션 :

ViewController에 프로토콜 UIGestureRecognizerDelegate 추가

class MyViewController: UIViewController, UIGestureRecognizerDelegate

에서 UIPanGestureRecognizer를 viewDidLoad만들고 delegateself로 설정 합니다.

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

    }
}

이것이 해결책입니다! 감사!
Hilalkah

8

내 경험상 "입력하는 동안 검색"과 유사하게 타이머가 가장 신뢰할 수있는 솔루션이라는 것을 알았습니다. 이동, 집기, 회전, 두드리기, 두 번 두드리기 등을위한 제스처 인식기를 추가 할 필요가 없습니다.

해결책은 간단합니다.

  1. 지도 지역이 변경되면 타이머 설정 / 초기화
  2. 타이머가 실행되면 새 지역의 마커를로드합니다.

    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

또 다른 가능한 해결책은 다음과 같이지도보기를 보유하는보기 컨트롤러에서 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;
        }
    }
}

경우에 따라 제스처 인식기를 사용하는 것보다 더 간단 할 수 있습니다.


6

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

물론 작동하려면 .h 파일에서 viewController를 UIGestureRecognizerDelegate로 만들어야합니다.

그렇지 않으면지도의 응답자는 제스처 이벤트를 듣는 유일한 사람입니다.


스토리 보드 솔루션에 적합합니다. 잘 UIGestureRecognizerStateBegan
Jakub 2014 년

5

맵뷰에서 제스처가 언제 종료되었는지 인식하려면 :

[ https://web.archive.org/web/20150215221143/http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/ )

이것은 사용자가지도를 확대 / 회전 / 드래그 한 후에 만 ​​데이터베이스 쿼리를 수행하는 데 매우 유용합니다.

나에게 regionDidChangeAnimated 메서드는 제스처가 완료된 후에 만 ​​호출되었으며 드래그 / 줌 / 회전 중에 여러 번 호출되지 않았지만 제스처 때문인지 아닌지 아는 것이 유용합니다.



이 방법은 저에게 효과적이지 않았습니다. mapView 지역이 코드에서 변경 되 자마자 사용자로부터 온 것으로 트리거됩니다 ...
Maksim Kniazev

5

이러한 솔루션 중 상당수는 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")
    }
}

이 하위 클래스를 가리 키도록 스토리 보드 파일의 클래스를 업데이트하고 다른 방법을 통해 만든 맵을 수정해야합니다.

의견에서 언급했듯이 Apple 은 하위 클래스 사용을 권장하지 않습니다MKMapView . 이것은 개발자의 재량에 속하지만,이 특정 사용법은지도의 동작을 수정하지 않으며 3 년 이상 무사히 저에게 도움이되었습니다. 그러나 과거 성능은 향후 호환성을 나타내지 않으므로 주의해야 합니다.


1
이것이 최선의 해결책 인 것 같습니다. 나는 그것을 테스트했고 정상적으로 작동하는 것 같습니다. 나는 Apple이 MKMapView를 하위 클래스 화하지 말 것을 권고한다는 것을 다른 사람들이 알아 두는 것이 좋다고 생각합니다. "MKMapView 클래스 자체를 하위 클래스 화해서는 안되지만 델리게이트 객체를 제공하여 맵 뷰의 동작에 대한 정보를 얻을 수 있습니다." 링크 : developer.apple.com/documentation/mapkit/mkmapview . 그러나 나는 MKMapView를 하위 클래스로 분류하지 말라는 그들의 조언을 무시하는 것에 대해 강한 의견을 가지고 있지 않으며 이에 대해 다른 사람들로부터 더 많은 것을 배울 수 있습니다.
Andrej

1
"애플이하지 말라고해도 이것이 최선의 해결책 인 것 같다"는 것은 아마도 최선의 해결책이 아닌 것 같다.
dst3p

3

Jano의 답변이 저에게 효과적 이었으므로 Objective C에 특히 능숙하지 않기 때문에 Swift 4 / XCode 9에 대한 업데이트 버전을 남겨 둘 것이라고 생각했으며 둘 중 하나가 아닌 다른 몇 가지가 있다고 확신합니다.

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 단계 : Jano가 올바르게 지적한대로 방법이 "드래그 당 50 회"호출되지 않도록합니다.

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

* 마지막 단계에서 @objc를 추가했습니다. XCode는 컴파일을 위해 함수에이 접두사를 강제합니다.


3

false이면 사용자가 드래그 한 맵에 애니메이션 속성을 확인할 수 있습니다.

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

사용자가지도를 확대했을 수 있습니다.
Victor Engel

2

나는 이것이 오래된 게시물이라는 것을 알고 있지만 Jano의 대답 엉 Pan 및 Pinch 제스처에 대한 Swift 4/5 코드입니다 .

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

즐겨!


앞서 말했듯이 이것은 줌을 인식하지 못하지만 대부분의 경우 Good Enough ™이어야합니다.
Skoua

1
줌에서도 작동합니다. 방금 테스트했습니다.)
Baran Emre

1

어떤 용도로 사용하든 항상지도 중앙에있는지도 중앙에 주석을 달려고했습니다. 위에서 언급 한 몇 가지 접근 방식을 시도했지만 그중 어느 것도 충분하지 않았습니다. 나는 결국 이것을 해결하는 매우 간단한 방법을 찾았습니다. Anna의 대답을 빌려 Eneko의 대답과 결합했습니다. 기본적으로 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()
}

0

여기에 코드를 입력하세요.지도와의 모든 상호 작용을 처리하는 가장 쉬운 방법으로 구현할 수있었습니다 (1 / 2 / N 손가락으로 탭핑 / 더블 / N 탭핑, 1 / 2 / N 손가락으로 팬, 핀치 및 회전

  1. gesture recognizer지도보기의 컨테이너 생성 및 추가
  2. gesture recognizer's delegate일부 개체 구현으로 설정UIGestureRecognizerDelegate
  3. 구현 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
}

-1

먼저 현재 뷰 컨트롤러가지도의 델리게이트인지 확인합니다. 따라서 Map View 델리게이트를 self로 설정하고 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
   }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.