-
Event CapturingiOS 2023. 10. 26. 13:29
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 Event Capturing에 대해 한번 알아보겠습니다 🙋🏻
우선 우리는 이전 포스팅에서 Event Bubbling이라는 개념에 대해 알아봤어요!
Event Bubbling을 가볍게 다시 리마인드 시켜볼까요?
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고 이어서 부모 요소의 핸들러가 동작하는것이 버블링의 원리 🫧
그렇기에 유저의 터치 이벤트를 처리하는 핸들러의 역할이 상단으로 전파될 수 있는것이죠.
만약 이벤트 버블링의 이해가 필요하시다면 이전 포스팅을 참고해주세요 🙏🏻
자 그럼 오늘의 주제인 이벤트 캡쳐링에 대해 알아보겠습니다!
Event Capturing
이벤트 버블링 외에 이벤트 캡쳐링이라는것도 존재합니다.
이벤트 버블링은 뭐라고 했죠!?
이벤트가 상위 요소로 전파되는것이 핵심이라고 했습니다.
그럼 반대로 이벤트 캡쳐링은 이벤트가 하위 요소로 전파되는것이 핵심입니다.
자바스크립트 문서에서 위와 같이 이벤트 캡쳐링의 흐름을 설명해주고 있습니다.
보시면, 최상단 Window로부터 발생한 이벤트가 하위인 <td>까지 흘러 내려오면서 전파되는 그림이죠.
즉, <td>를 클릭 시 이벤트 자체가 최상위인 Window부터 아래로 전파되어 내려오는것이 캡쳐링입니다.
그와 더불어서 해당 이벤트가 타깃 요소인 <td>에 도달하면 실행 후 다시 위로 전파되는것이 버블링이죠.
결국 이러한 유기적인 흐름 관계를 통해 요소에 할당된 이벤트 핸들러가 호출됩니다 😄
문서에서는 이러한 캡쳐링 단계를 이용하는 경우는 거의 흔치 않다고해요!
그럼에도 실제 이벤트 캡쳐링 단계에서 이벤트를 잡아내기 위해서는 자바스크립트에선 아래와 같이 addEventListener의 capture 옵션을 true로 설정해줘야 합니다.
elem.addEventListener(..., {capture: true}) elem.addEventListener(..., true)
여기서 true 값이면 핸들러가 캡쳐링 단계에서 동작하고 false 값이면 핸들러가 버블링 단계에서 동작한다고 합니다.
조금 더 예시를 살펴볼까요?
<style> body * { margin: 10px; border: 1px solid blue; } </style> <form>FORM <div>DIV <p>P</p> </div> </form> <script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`)); } </script>
해당 코드가 있을때 스텝을 살펴보죠.
1️⃣ <p> 클릭 시 HTML -> BODY -> FORM -> DIV인 캡쳐링 단계를 거침
2️⃣ P (타깃 단계) - 현재 캡쳐링과 버블링에 모두 리스너를 걸었기에 두번 호출
3️⃣ DIV -> FORM -> BODY -> HTML인 버블링 단계를 거침
위 순서와 같이 캡쳐링과 버블링 모두 이뤄집니다.
자 그럼 이런 흐름을 iOS에서는 어떤 코드를 통해 살펴보고 체킹해볼 수 있을까요?
import UIKit class MyCustomView: UIView { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) 🙋🏻 터치 이벤트 발생 시 호출 print("터치 이벤트 발생! 캡쳐링 ㄱㄱ~") 🙋🏻 이벤트를 중지 event?.stopImmediatePropagation() } }
위 코드를 보면 탭이 발생하면 최상위 단계로 엮여있는 touchesBegan이라는 메서드가 호출되게 되죠.
그리고 하위에서는 뭐, 원래대로라면 해당 버튼에서 심어놓은 동작들이 발생하게되고 위 touchesBegan에서의 기본 동작은 없을거에요.
그런데 여기 코드를 오버라이드하여 어떠한 구현을 해준다면 이벤트 캡쳐를 해당 메서드에서도 수행하게 됩니다.
즉, 해당 메서드들에서 이벤트 캡쳐링이 일어나며 어떠한 원하는 핸들러 동작도 하는것이죠!
그런데 사실은 iOS에서는 이벤트 캡쳐링이라는 개념이 따로 없어요.
이 이벤트 캡쳐링도 버블링처럼 웹 개발에서 주로 사용되는 개념입니다.
iOS에서는 Event Delivery Chain이라는 비슷한 개념이 존재합니다.
특정 이벤트가 발생했을 때, 이벤트가 어떻게 뷰 계층을 통해 전달되는가이죠.
class ParentView: UIView { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { print("Parent View - Touches Began") super.touchesBegan(touches, with: event) } } class ChildView: UIView { override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { print("Child View - Touches Began") super.touchesBegan(touches, with: event) } }
아주 익숙하겠지만, ParentView가 ChildView를 포함하고 있다고 가정해볼께요.
여기서 ChildView의 터치 이벤트가 발생하면, ChildView의 touchesBegan 메서드가 호출되겠죠?
그런데 여기서 super.touchesBegan()을 호출하고 있습니다.
그렇기에 상위 뷰인 ParentView로 이벤트가 전달이됩니다.
이 흐름으로 이벤트가 뷰 계층을 따라 전달되는것을 볼 수 있어요.
아주아주 익숙하고 그냥 생각없이 써왔지만 이런 흐름들이 존재하는걸 확인할 수 있죠!
마무리
iOS에서는 어떻게 이벤트들이 뷰 계층을 통해 어떤 근본적인 원리로 전달되고 이런걸 뭐라하는지 버블링과 캡쳐링을 통해 알아볼 수 있는 하나의 좋은 과정이였습니다 😀
레퍼런스
'iOS' 카테고리의 다른 글
ATS (App Transport Security) 톺아보기 (33) 2023.11.14 인앱리뷰(In-App Review) 연동하기 (49) 2023.11.09 Event Bubbling (44) 2023.10.23 메모리 덤프 분석 (feat. fridump) (44) 2023.09.14 앱 출시 - 숏스(SHORT NEWS) (feat. Mash-Up) (30) 2023.07.24