Program Club

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

proclub 2020. 10. 10. 12:29
반응형

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


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

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

미리 감사드립니다


상기 봐 MKMapViewDelegate의 참조입니다.

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

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

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


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

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

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

(그냥) @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
    }
}

의 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

    }
}

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

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


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

해결책은 간단합니다.

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

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로 만들어야합니다.

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


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

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


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

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

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

http://b2cloud.com.au/tutorial/mkmapview-determining-whether-region-change-is-from-user-interaction/

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

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


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는 컴파일을 위해 함수에이 접두사를 강제합니다.


enter code hereI managed to do implement this in easiest way, which handles all interaction with map (tapping/double/N tapping with 1/2/N fingers, pan with 1/2/N fingers, pinch and rotations

  1. Create gesture recognizer and add to map view's container
  2. Set gesture recognizer's delegate to some object implementing UIGestureRecognizerDelegate
  3. Implement gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) method
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
}

I was trying to have an annotation in the center of the map that is always at the center of the map no matter what the uses does. I tried several of the approaches mentioned above, and none of them were good enough. I eventually found a very simple way of solving this, borrowing from the Anna's answer and combining with Eneko's answer. It basically treats the regionWillChangeAnimated as the start of a drag, and regionDidChangeAnimated as the end of one, and uses a timer to update the pin in real-time:

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

First, make sure your current view controller is a delegate of the map. So set your Map View delegate to self and add MKMapViewDelegate to your view controller. Example below.

class Location_Popup_ViewController: UIViewController, MKMapViewDelegate {
   // Your view controller stuff
}

And add this to your map view

var myMapView: MKMapView = MKMapView()
myMapView.delegate = self

Second, add this function which is fired when the map is moved. It will filter out any animations and only fire if interacted with.

func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
   if !animated {
       // User must have dragged this, filters out all animations
       // PUT YOUR CODE HERE
   }
}

I know this is an old post but here my Swift 4/5 code of Jano's answer whit Pan and Pinch gestures.

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

Enjoy!

참고URL : https://stackoverflow.com/questions/5556977/determine-if-mkmapview-was-dragged-moved

반응형