야매 iOS

[iOS/Swift] RxGesture - Gesture가 동시에 인식되는 문제

the Cosmos 2023. 8. 7. 20:14

문제

environment

UIScrollView가 있고 subview로 UITextField를 가짐

UIScrollView에 rx.tapGesture를 사용해 TapGesture 적용

  • UIScrollView를 탭했을 때 키보드를 내리기 위해

problem

UITextField를 터치했을 때 interaction이 어색함

해결

TL;DR

scrollView.rx.tapGesture() // 수정 전 

scrollView.rx.tapGesture(configuration: { _, config in // 수정 후
    config.simultaneousRecognitionPolicy = .never
})

config.simultaneoulsyRecognitionPolicy 프로퍼티에 .never를 넣어주면 됨

RxGesture

RxGesture는 configuration을 설정할 수 있는 구조로 되어 있음

extension Reactive where Base: RxGestureView {
    public func tapGesture(configuration: TapConfiguration? = nil) -> TapControlEvent {
        gesture(make(configuration: configuration))
    } 
}

Configuration

RxGesture에서 Configuration은 클로저로 구성되어 있다.

  • GestureRecognizer와 Delegate를 인자로 받는 클로저
typealias Configuration<Gesture: RxGestureRecognizer> = (Gesture, GenericRxGestureRecognizerDelegate<Gesture>) -> Void

사용할 때 GestureRecognizer 또는 Delegate에 접근해서 값을 커스텀할 수 있다.

GenericRxGestureRecognizerDelegate

UIGestureRecognizerDelegate에 대응되는 값들을 갖고 있다

struct GestureRecognizerDelegatePolicy {
    private init policy: @escaping PolicyBody) {
        self.policy = policy
    }    

    public static var always: GestureRecognizerDelegatePolicy<PolicyInput> {
        .init { _ in true }
    }

    public static var never: GestureRecognizerDelegatePolicy<PolicyInput> {
        .init { _ in false }
    }

    func isPolicyPassing(with args: PolicyInput) -> Bool {
        policy(args)
    }
}

final class GenericRxGestureRecognizerDelegate<Gesture: RxGestureRecognizer> RxGestureRecognizerDelegate {
    ...

    simultaenousRecognitionPolicy: GestureRecognizerDelegatePolicy<(Gesture, RxGestureRecognizer)> = .always

    public func gestureRecognizer(
        _ gestureRecognizer: RxGestureRecognizer,
        shouldRecognizeSimultaenouslyWith otherGestureRecognizer: RxGestureRecognizer
    ) -> Bool {
        simultaenousRecognitionPolicy.isPolicyPassing (with: (gesture as! Gesture, otherGestureRecognizer))
    }
}

해결

shouldRecognizeSimultaenouslyWith otherGestureRecognizer 의 default value가 .always 였으므로 이벤트를 UITextField도 받고 UIScrollView도 받아서 생기는 문제였음

→ 프로퍼티의 값을 .always(true)에서 .never(false)로 변경하니 UITextField의 Gesture와 UIScrollView에 적용된 TapGesture가 동시에 인식되지 않아 문제 해결

 

UIGestureRecognizerDelegate의 gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) 디폴트 값은 false이지만 RxGesture에선 true로 설정되어 있음

실제 Delegate과 defaultValue가 다르므로 이 메서드뿐만 아니라 다른 메서드를 사용할 때도 주의해서 사용해야 함