관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UITapGestureRecognizer 제스쳐에서 subviews들은 무시하는 방법 isDescendant(of:), gestureRecognizer(_:, shouldReceive:) 본문

iOS 기본 (swift)

[iOS - swift] UITapGestureRecognizer 제스쳐에서 subviews들은 무시하는 방법 isDescendant(of:), gestureRecognizer(_:, shouldReceive:)

jake-kim 2022. 3. 29. 23:57

가장 subview인 흰색 뷰만 tap 이벤트 반응

예제에서 편의를 위해 사용한 프레임워크

  pod 'SnapKit'
  pod 'Then'
  pod 'RxSwift'
  pod 'RxCocoa'
  pod 'RxGesture'

UITapGestureRecognizer를 사용하면서 발생되는 문제

  • 맨 밑에 깔려있는 view에 tapGesture를 등록했을 때, subviews들을 tap해도 이벤트가 발생하는 문제가 존재

ex) 흰색 뷰에 tapGesture를 등록했을 때, layer2, layer1 뷰들을 탭해도 이벤트가 발동

- 상단에 팝업이 있고, 밑에 배경이 있을 때 배경을 탭한 경우 팝업을 숨기고 싶은 경우에 아래와 같이 처리되면 문제가 발생 (layer2만 탭해도 사라지는 현상이 발생할 것)

흰색뷰에 tap 제스쳐를 등록해도, subview들 탭에 반응

  • Responder chain의 기본 원리는 superview부터 체크하기 때문에, layer2가 탭되어도 흰색 view의 탭 이벤트가 발생하는 것

이미지 출처: https://lena-chamna.netlify.app/post/hit_testing_in_ios/

해결 방법

  • gesture의 delegate 메소드에서 이벤트를 처리할 것인지 분기문을 추가
    • 가장 밑에 깔려있는 흰색 뷰, layer1, layer2 뷰가 있을 때, touch뷰가 layer1을 포함하고 있는 터치영역이면 false를 리턴하고, 포함하고 있지 않으면 true를 리턴
    • layer1만 해놓으면 그 subviews들이 탭되어도 layer1에 속해있으므로 false를 리턴
// gestureRecognizer.delegate = self 필요
extension ViewController: UIGestureRecognizerDelegate {
  func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
    guard touch.view?.isDescendant(of: self.layer1View) == false else { return false }
    return true
  }
}
Observable
  .merge(
    self.layer1View.rx.tapGesture(configuration: { [weak self] gestureRecognizer, delegate in
      guard let ss = self else { return }
      gestureRecognizer.delegate = ss
      delegate.simultaneousRecognitionPolicy = .never
    })
    .asObservable(),
    self.view.rx.tapGesture(configuration: { [weak self] gestureRecognizer, delegate in
      guard let ss = self else { return }
      gestureRecognizer.delegate = ss
      delegate.simultaneousRecognitionPolicy = .never
    })
    .asObservable()
  )
  .when(.recognized)
  .bind { [weak self] _ in self?.count = (self?.count ?? 0) + 1 }
  .disposed(by: self.disposeBag)

* 전체 코드: https://github.com/JK0369/ExRecognizer

 

* 참고

https://github.com/RxSwiftCommunity/RxGesture

https://lena-chamna.netlify.app/post/hit_testing_in_ios/

https://stackoverflow.com/questions/15814697/uitapgesturerecognizer-tap-on-self-view-but-ignore-subviews

Comments