본문 바로가기

IOS Swift

iOS - 이벤트 처리 흐름

이벤트 처리 흐름

  1. 사용자가 앱을 사용하면서 이벤트를 생성합니다. (터치, 드래그 등)
  2. Operating system(운영체제)이 포트를 통해 Event queue에 이벤트 정보(클릭, 드래그 등)를 App으로 전달해줍니다.
  3. Event queue에  쌓인 이벤트를 Main run loop가 처리하여 앱 객체에게 전달해줍니다.
  4. 앱 객체는 전달받은 이벤트로 어떤 행동을 할 것인지 정해지고 그것이 화면으로 보여집니다.

대략적인 흐름은 이렇습니다. 여기서 4번을 더 깊이 파고 들어가 보겠습니다.


앱 객체로 전달받은 후의 이벤트 흐름 

UIKit 은 이벤트를 UIResponder객체에게 보냅니다. 여기서 UIResponder 객체는 UIViewController, UIView, UIWindow, UIApplication 처럼 UIResponder를 상속받고 있는 객체 입니다.

UIView

 

근데.. 많고 많은 UIResponder 객체중에 누구에게 보낼까요? 

UIKit은 hit-testing를 사용하여 터치 이벤트가 발생하는 위치를 결정합니다. 특히 UIKit은 터치 위치를 뷰 계층 구조의 뷰 객체 경계와 비교합니다. 뷰 계층 구조를 순회 하는 메서드는 지정된 터치를 포함하는 가장 깊은 하위 뷰를 찾고 터치 이벤트에 대한 첫 번째 응답자가 됩니다. (hit-testing 밑에 설명)

아하 결론은 앱 화면을 터치하면 UIKit -> UIResponder로 이벤트를 전달한다는 것입니다.

엇 그럼 터치는 그러한데 스크롤같은 경우는 터치한 상태에서 계속 움직이는데 그건 어떻게 처리 될까요..?

터치한 순간 UIKit은 UITouch 객체를 만들고 뷰와 연결시킵니다. 또한 터치 위치 또는 다른 피라미터가 변했을 때 UIKit은 같은 UITouch 객체를 새로운 정보로 업데이트 합니다. view는 안바뀝니다. (심지어 터치 위치가 처음 터치 했던 뷰 밖으로 이동했을 때도) 터치가 끝났을 때, UIKit은 UITouch 객체를 해제합니다.

한마디로 한번 스크롤 하던 뭐를 하던 터치하고 화면에서 손가락이 떨어질 때 까지 UITouch가 재사용 된다는 거네요. 

2개의 세로 스크롤 뷰

어쩐지 위에 처럼 스크롤뷰 2개가 있을 때 B -> A 쪽으로 스크롤 해도 B에만 이벤트가 전달 되더라구요. 스크롤 할 때 A 지역으로 넘어 갔지만 UITouch는 B 객체로 계속 재사용 되는 거였네요. 😗 

아 근데 왜 갑자기 UITouch가 나온거죠? 애플 문서보니까 UIEvent도 있던데 어떤 관계가 있을까요?

 

UITouch, UIEvent  관계

UITouch

화면에서 발생하는 터치의 위치, 크기, 이동, 세기를 나타내는 객체

 

UIEvent

앱에서 사용자와의 하나의 상호작용을 나타내는 객체 (화면에 손가락을 터치하고 땔 때 까지가 하나)

 

UIEvent 이벤트 종류

대표적인 3 가지

extension UIEvent {
    public enum EventType : Int {
        case touches = 0

        case motion = 1

        case remoteControl = 2

        @available(iOS 9.0, *)
        case presses = 3

        @available(iOS 13.4, *)
        case scroll = 10

        @available(iOS 13.4, *)
        case hover = 11

        @available(iOS 13.4, *)
        case transform = 14
    }
}

 

UIEvent 속성

//Getting the Touches for an Event

var allTouches: Set<UITouch>?
//Returns all touches associated with the event.

func touches(for: UIView) -> Set<UITouch>?
//Returns the touch objects from the event that belong to the specified given view.

func touches(for: UIWindow) -> Set<UITouch>?
//Returns the touch objects from the event that belong to the specified window.

func coalescedTouches(for: UITouch) -> [UITouch]?
//Returns all of the touches associated with the specified main touch.

func predictedTouches(for: UITouch) -> [UITouch]?
//Returns an array of touches that are predicted to occur for the specified touch.

아 UIEvent 속성에 UITouch를 여러개 담을 수 있게 되어 있네요.

 

정리를 해보자면 UIEvent는 터치 이벤트, 모션이벤트, 원격 조종 이벤트 등등을 담을 수 있고 UITouch는 화면에서의 터치 이벤트만 담는 객체 입니다. UITouch는 UIEvent로 감싸서 이벤트가 전달 된 다는 것!

 // 매개변수로 UITouch가 아닌 UIEvent
 override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
	let hitView = super.hitTest(point, with: event)

	print(event?.type.rawValue)
        
	return  hitView
}

 


정리

터치 -> UIKit이 UITouch 생성 -> UIApplicaion -> UIWindow ~~~ hit-testing ~~ -> 눌린 UIView

 

  1.  화면에서 터치가 발생한다.
  2. UIKit에서 UITouch 객체를 생성한다. (UIEvent로 감싸져서 들어옵니다.) ex)hitTest(_ point: CGPoint, with event: UIEvent?)
  3. hit-test를 통해 눌린 UIResponder객체를 알아내고 UITouch객체와 연결시킨다.
  4. 이벤트를 전달받은 UIResponder객체는 사용자에게 보여줄 행동을 취한다.

 

 

Hit-Testing

특정 point를 포함하는 뷰 계층에서 응답자의 가장 먼 자손을 반환한다. ?? 

정의만 봐서는 모르겠으니 HitTesting을 어떻게 하는지 알아보겠습니다.

 

일단 초록색 부분을 클릭하면 겹치는 뷰는 부분은 UIWindow, MainView, ViewA, ViewB 입니다. 그럼 hit test를 시작해 봅시다.

  1. UIWindow hit test -> 터치 포인트가 UIWindow안에 포함되므로 true, 안쪽 뷰로 들어감
  2. MainView -> 터치 포인트가 MainView안에 포함되므로 true, 안쪽 뷰로 들어감
  3. MainView를 보니까 내부에 ViewA, ViewB, ViewC가 있습니다. 그럼 제일 아래쪽에 있는 것부터 합니다.
  4. MainView의 제일 아래에는 ViewC가 있는데 -> 터치 포인트가 ViewC 밖이므로 false, 그 위에 뷰로 넘어감
  5. 그다음은 중간에 있는 ViewB -> 터치 포인트가 ViewB안에 포함되므로 true, 안쪽 뷰로 들어감
  6. ViewB.2 -> 터치 포인트가 ViewB.2 밖이므로 false 그 위에 뷰로 넘어감
  7. ViewB.1 -> hit test true 

즉, Window부터 hit test 하면서 원하는 view까지 내려옵니다.

이렇게 hit-testing을 통하여 터치 이벤트가 발생할 위치를 어떻게 정하는 지 까지 알아보았습니다. 

 

참고

UIResponder와 UIResponderChain의 이벤트 처리

hittest

반응형

'IOS Swift' 카테고리의 다른 글

Fastlane 배포 자동화 적용(feat. iOS)  (0) 2022.04.21
RxSwift 정리  (0) 2022.03.30
App Store 개발자 등록하기  (0) 2021.12.20
아스키코드와 유니코드  (0) 2021.11.20
앱 실행 흐름  (1) 2021.10.07