UITapGestureRecognizer는 self.view를 탭하지만 하위보기는 무시합니다.


95

self.view (view of UIViewCotroller)를 두 번 탭하면 일부 코드를 호출하는 기능을 구현해야합니다 . 그러나이 뷰에 다른 UI 개체가 있고 모든 인식기 개체를 첨부하고 싶지 않은 문제입니다. 나는이 방법을 내 관점에서 제스처를 만드는 방법을 발견했으며 어떻게 작동하는지 알고 있습니다. 지금 나는 하위보기를 무시 하고이 인식기를 만들기 위해 선택할 방법을 선택하는 핸디캡 앞에 있습니다. 어떤 아이디어? 감사.

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[self.view addGestureRecognizer:doubleTap];

1
잘 모르겠지만 인식기에서 cancelsTouchesInView를 NO로 설정해 보셨습니까? 그래서 [doubleTap setCancelsTouchesInView : NO];
JDx 2013

답변:


144

객체 UIGestureRecognizerDelegate내부 에 프로토콜을 채택 self하고 뷰를 확인하기 위해 아래 메소드를 호출해야합니다. 이 메서드 내에서보기를 확인 touch.view하고 적절한 부울 (예 / 아니요)을 반환합니다. 이 같은:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    if ([touch.view isDescendantOfView:yourSubView]) {
        return NO;
    }
    return YES;
}

편집 : @Ian의 대답도 확인하십시오!

스위프트 5

// MARK: UIGestureRecognizerDelegate methods, You need to set the delegate of the recognizer
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
     if touch.view?.isDescendant(of: tableView) == true {
        return false
     }
     return true
}

한 가지 더 질문하면 작동해야하는 내 뷰에 버튼이 거의 없습니다. 우선 순위를 어떻게 설정할 수 있습니까?
Matrosov Alexander 2013-04-04

당신의 버튼을 자신의 제스처 인식기를 (가능성), 자신의 내부로 파고을 잡은에서 문서를 읽을 수있는 경우 -[UIGestureRecognizer requireGestureRecognizerToFail:]그러나 그들을 mod'ing없이 작동 할 수 있습니다
bshirley

몇 시간 동안 이것을 찾고 있었다! 감사! ;)
MasterRazer 2013

귀하의 경우 if테스트가 실패, 구현은 BOOL을 반환하지; 적절한 스타일을 얻으려면 if 블록 (사용 { }) 또는 else분기 에서 YES를 반환 합니다. 그래도 고마워서 약간의 독서를 구했습니다.
RobP

2
보기는 자체의 자손이므로 작동하지 않습니다 ... (전체 접근 방식은 괜찮습니다. 다른 답변 참조)
Ixx

107

또 다른 접근 방식은 후손이 조건을 통과하지 않기 때문에 터치의보기가 제스처보기인지 비교하는 것입니다. 멋지고 간단한 한 줄짜리 :

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == gestureRecognizer.view
}

2
이것은 받아 들인 대답보다 더 잘 작동합니다. 훨씬 더 간결합니다.
Suman Roy

1
@Ian이 메서드는보기를 탭할 때 호출되지 않습니다.
Praburaj

1
아주 간결한 대답!
scurioni

받아 들여진 대답은 작동하지 않았지만 이것은 나를 위해 완벽하게 작동했습니다.
Shalin Shah

제스처 인식기의 대리자하기 전에 설정해야합니다 @Prabu propably 때문에
Kubba

23

그리고 Swift 변형의 경우 :

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if touch.view.isDescendantOfView(yourSubView){
        return false
    }
    return true
}

알아두면 좋은 점 은 수신자가 지정된 뷰의 하위 뷰인지 또는 해당 뷰와 동일한 지 나타내는 값을 isDescendantOfView반환합니다 Boolean.


1
클래스에서 UIGestureRecognizerDelegate를 설정하는 것을 잊지 마십시오!
Fox5150

4
if 문을 수행하는 대신 이것을 반환 할 수 없습니까? return! touch.view.isDescendant (of : gestureRecognizer.view) 또한 swift 3의 새 구문 ^^
EdwardSanchez

12

Swift 5 및 iOS 12 UIGestureRecognizerDelegate에는 gestureRecognizer(_:shouldReceive:). gestureRecognizer(_:shouldReceive:)다음과 같은 선언이 있습니다.

제스처 인식기가 터치를 나타내는 개체를 받아야하는지 대리인에게 문의하십시오.

optional func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool

아래의 전체 코드는 gestureRecognizer(_:shouldReceive:). 이 코드를 사용하면 ViewController의 뷰 (포함 imageView) 의 하위 뷰를 탭 해도 printHello(_:)메서드가 트리거되지 않습니다 .

import UIKit

class ViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(printHello))
        tapGestureRecognizer.delegate = self
        view.addGestureRecognizer(tapGestureRecognizer)

        let imageView = UIImageView(image: UIImage(named: "icon")!)
        imageView.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
        view.addSubview(imageView)

        // ⚠️ Enable user interaction for imageView so that it can participate to touch events.
        // Otherwise, taps on imageView will be forwarded to its superview and managed by it.
        imageView.isUserInteractionEnabled = true
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        // Prevent subviews of a specific view to send touch events to the view's gesture recognizers.
        if let touchedView = touch.view, let gestureView = gestureRecognizer.view, touchedView.isDescendant(of: gestureView), touchedView !== gestureView {
            return false
        }
        return true
    }

    @objc func printHello(_ sender: UITapGestureRecognizer) {
        print("Hello")
    }

}

의 대체 구현은 gestureRecognizer(_:shouldReceive:)다음과 같습니다.

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return gestureRecognizer.view === touch.view
}

그러나이 대체 코드 touch.view는의 하위보기 인지 확인하지 않습니다 gestureRecognizer.view.


10

완전한 신속한 솔루션 (대리인이 구현되고 인식기에 대해 설정되어야 함) :

class MyViewController: UIViewController UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(onBaseTapOnly))
        doubleTapRecognizer.numberOfTapsRequired = 2
        doubleTapRecognizer.delegate = self
        self.view.addGestureRecognizer(doubleTapRecognizer)
    }

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
        if touch.view.isDescendantOfView(self.view){
            return false
        }
        return true
    }

    func onBaseTapOnly(sender: UITapGestureRecognizer) {
        if sender.state == .Ended {
            //react to tap
        }
    }
}

6

터치하는 CGPoint를 사용하는 변형 (SWIFT 4.0)

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {

// Get the location in CGPoint
    let location = touch.location(in: nil)

// Check if location is inside the view to avoid
    if viewToAvoid.frame.contains(location) {
        return false
    }

    return true
  }
}

4

명확한 신속한 방법

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    return touch.view == self.view
}

적어도 swipeGestureRecognizer를 사용하면 iOS 13에서 호출되지 않습니다.
Chewie The Chorkie

Ian의 대답과 어떻게 다른가요?
sweetfa

3

있습니다 gestureRecognizer의 API가 변경되었습니다 :

gestureRecognizer (_ : shouldReceive :)

첫 번째 매개 변수의 외부 레이블에 대한 밑줄 (건너 뛰기) 표시기에 특히주의하십시오.

위에 제공된 많은 예를 사용하여 이벤트를받지 못했습니다. 다음은 현재 버전의 Swift (3+)에서 작동하는 예입니다.

public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    var shouldReceive = false
    if let clickedView = touch.view {
        if clickedView == self.view {
            shouldReceive = true;
        }
    }
    return shouldReceive
}

2

또한 위의 솔루션과 함께 User Interaction Enabled하위보기 를 확인하는 것을 잊지 마십시오 .

여기에 이미지 설명 입력


2

나는 아이 뷰에서 제스처를 막아야했다. 효과가 있었던 유일한 방법은 첫 번째보기를 허용 및 유지하고 다음 모든보기에서 제스처를 방지하는 것입니다.

   var gestureView: UIView? = nil

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        if (gestureView == nil || gestureView == touch.view){
            gestureView = touch.view
            return true
        }
        return false
     }

1

스위프트 4 :

touch.view 이제 선택 사항이므로 @Antoine의 답변을 기반으로합니다.

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    if let touchedView = touch.view, touchedView.isDescendant(of: deductibleBackgroundView) {
        return false
    }
    return true
}

0

'더블 탭 인식기'가 버튼 및 / 또는 기타 컨트롤과 충돌하지 않도록하려면 다음 self과 같이 설정 UIGestureRecognizerDelegate하고 구현할 수 있습니다 .

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
{
    return !(touch.view is UIControl)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.