공유 화이트보드를 구현하면서, 저희는 많은 터치 이벤트를 처리해야 했습니다. 이 과정에서 제스쳐 인식기부터 좌표변환, transform 등 많은 개념을 학습하고 적용했는데요, 그 과정을 아래에서 소개합니다.
Airplain에는 많은 뷰들이 겹쳐서 올라가기 때문에, 일부 View에서 hitTest를 오버라이딩하고 있습니다. hitTest란 뭘까요?
화이트보드를 편집할 수 있다는 기능이 있는 만큼, AirplaIN은 많은 제스처 인식기를 사용하고 있습니다. 제스쳐 인식기란 뭘까요?
<aside>
A gesture recognizer decouples the logic for recognizing a sequence of touches (or other input) and acting on that recognition. When one of these objects recognizes a common gesture or, in some cases, a change in the gesture, it sends an action message to each designated target object.
</aside>
쉽게 설명하면, 사용자의 입력과 이후 행할 동작(action)을 분리해주는 객체이다. 제스쳐를 인식하면, 미리 지정한 대상에게 동작(action)을 실행하라고 알려주는 객체가 Gesture Recognizer 입니다. 제스쳐 인식기는 아래와 같은 특징이 있습니다.
하나의 제스쳐 인식기는 하나의 뷰에 추가할 수 있습니다. (하나의 제스쳐 인식기를 여러 뷰에 등록할 수 없음!)
하지만 하나의 제스쳐 인식기가 여러개의 target-action 쌍을 갖는 것은 가능합니다! 아래와 같은 예시가 있습니다. (참고로 저희 프로젝트에서는 아래와 같은 경우는 없습니다)
let tapGesture = UITapGestureRecognizer()
tapGesture.addTarget(self, action: #selector(handleTap))
tapGesture.addTarget(aView, action: #selector(aView.handleTap))
view.addGestureRecognizer(tapGesture)
윈도우는 터치 이벤트를 제스처 인식기에 먼저 전달하고, 다음으로 해당 제스처 인식기가 연결된 hit-tested 뷰에 전달합니다.
제스처가 터치 이벤트를 먼저 수신하며, 제스처를 인식하지 못한 경우에만 hitTested 된 뷰로 터치 이벤트를 전달합니다. 제스처를 인식한 경우, 남은 터치 sequence를 취소하여 뷰에 전달하지 않습니다.
제스쳐 인식기의 동작은 cancelsTouchesInView, delaysTouchesBegan, delaysTouchesEnded의 값에 따라 동작합니다.
cancelsTouchesInView
제스쳐 인식기가 제스쳐를 인식하면, 해당 제스처의 나머지 터치 이벤트를 뷰에서 분리합니다 (따라서 윈도우가 뷰에게 터치 이벤트를 전달X) 윈도우는 이전에 뷰에게 전달된 터치 객체들을 touchesCancelled(_:with:)
를 통해 취소합니다.
delaysTouchesBegan
제스처 인식기가 터치 이벤트를 분석하는 동안, 그리고 제스처 인식을 실패하지 않는 동안 View에게 UITouch.Phase.began
상태인 터치 객체의 전달을 delay합니다. 제스처 인식기가 제스쳐 인식에 실패하면, 윈도우가 View의 touchesBegan(_:with:)
를 통해 터치 객체들을 전달합니다.
delaysTouchesEnded
제스처 인식기가 터치 이벤트를 분석하는 동안, 그리고 제스처 인식을 실패하지 않는 동안 View에게 UITouch.Phase.ended
상태인 터치 객체의 전달을 delay합니다. 제스처 인식기가 제스쳐 인식에 실패하면, 윈도우가 View의 touchesEnded(_:with:)
를 통해 터치 객체들을 전달합니다.