Responder Chain

이원희·2020년 12월 16일
1

📱 iOS

목록 보기
9/24
post-thumbnail

touchesBegan()에 대해 공부하다 Responder Chain에 대한 얘기가 나와서 포스팅해본다!
우선 Apple Article을 기준으로 작성했다.

우선 Article의 Overview에 대해 살펴보고 하나씩 알아가보자!

Overview

  • App은 Responder Object를 사용해 event를 수신하고 처리한다.
  • Responder ObjectUIResponder 클래스의 모든 instance이다.
  • Responderraw event data를 수신하고 event를 처리하거나 다른 Responder Object로 전달해야한다.
  • App이 event를 수신하면 UIKit는 자동으로 first responder인 가장 적절한 Responder Object로 event를 알린다.
  • 처리되지 않은 event는 active Resopnder Chain 안에 있는 Responder에서 다른 Responder로 전달된다.

Overview를 읽어보면 Responder Object가 반복적으로 나온다.
Resopnder Object에 대해서 먼저 알아보자!


Responder Object

Responderevent를 핸들링하고 event에 반응할 수 있는 객체이다.

  • Overview에도 나와 있듯 Responder ObjectUIResponder에서 상속된 class들의 instance이다.
  • UIResponder에서 상속된 class는 event 핸들링을 위한 interface와 Responder들의 기본적인 행위를 정의한다.
  • 위의 그림에서 보이듯 UIResponder에서 상속된 class인 UIApplication, UIViewController 객체 뿐만 아니라 모든 UIView 객체들(UIWindow 객체 포함)은 Responder이다.
    (이는 거의 대부분의 UI 객체들이 event를 수신하고 처리할 수 있다는 것을 뜻한다고 한다.)

event가 발생하면 어떻게 될까?

event가 발생하면 UIKit는 event 핸들링을 위해 App의 Responder Object에게 event를 전달한다.

event의 종류에는 Touch, Press, Shake-motion, Remote-control, Editing menu message가 있다.
이 부분에 대해서는 아래에서 다시 한 번 다뤄보자!

위와 같은 종류로 나눠진 event들을 핸들링하기 위해서는 Responder해당 event에 대응되는 Method들을 override하여 구현해야 한다.

우리는 이전 포스팅에서 다뤘던 touchesBegan(_:with:), touchesMoved(_:with:), touchesEnded(_:with:), touchesCancelled(_:with:) Method를 override해서 touch event를 핸들링했다.
(touch event는 Responder에서 touch의 변화를 트래킹하고 App의 interface를 적절히 업데이트하기 위해 UIKit에서 제공하는 event 정보를 이용한다.)


Responder Chain?!

우리는 Responder가 무엇인지 알았다.

event를 핸들링하고 event에 반응할 수 있는 객체

Responder에서 처리되지 않은 event는 active Responder Chain 안에 있는 Responder에서 다른 Responder로 전달된다는데 Responder Chain은 뭘까?

  • Responder들은 event 핸들링 뿐만 아니라 처리되지 않은 event를 App의 다른 곳으로 forwarding하는 일도 한다.
  • Responder Chain은 event나 액션 메시지를 App의 다른 Object로 전송해서 핸들링하도록 한다.
  • 정해진 Responder가 event를 핸들링하지 않을 경우, 해당 Responder는 event를 Responder Chain으로 엮인 다음 Object에게로 forwarding한다.
  • 이런 event나 액션 메시지는 처리될 때까지 계속해서 chain의 상위 Object들로 이동한다.
  • 마지막까지 처리되지 않을 경우에는 App이 해당 event나 액션 메시지를 버린다.

Article에서는 아래의 그림을 통해 Responder Chain을 설명한다.
우리도 아래의 그림으로 event가 Responder Chain을 따라 이동하는 내용을 따라가보자.

1개 label, 1개 textField, 1개 button, 2개 background view로 이뤄진 화면이 있다.

사용자가 textField를 터치해서 event가 발생했다.

  1. textField가 event를 핸들링하지 않으면, UIKit는 textField의 부모인 UIView 객체에게로 event를 보낸다.
  2. event를 전달받은 UIView에서도 event가 핸들링되지 않으면 window의 root view에게로 event를 보낸다.
  3. root view에서 Responder Chain은 event를 UIWindow로 보내기 전에 root view를 소유하고 있는 view Controller에게로 event를 보낸다.
  4. 만약 window가 event를 처리하지 못하면 UIKit는 event를 UIApplication 객체에게로 보낸다.
  5. app delegate가 UIResponder의 인스턴스이고 이전에 event를 받은 적이 없다면 app delegate까진 전달된다.

이런 과정을 통해 event가 핸들링될 때까지 상위로 이동하는 모습을 볼 수 있다.


First Responder

UIKit는 받은 event의 종류에 따라서 특정 객체를 해당 event의 first responder로 지정한다.

Responder가 event를 받으면 이를 반드시 처리하거나, 다른 Responder가 처리할 수 있도록 넘겨야한다.
App이 event를 받으면 UIKit는 적절한 Responder를 지정해서 event를 넘겨 처리를 하게 한다.
처음으로 event를 받는 Responderfirst responder라고 한다.
즉 event를 처음으로 받을 (UIKit가 지정한) 적절한 Responderfirst responder라고 할 수 있다.

그렇다면 이렇게 표현할 수도 있을거 같다.

first responder가 event를 처리하지 못한다면 처리할 수 있는 Responder가 나올 때 까지 연쇄적으로 Responder Chain으로 연결된 다음 Responder에게 넘어간다.

App에서 event를 처음 받는 Responder 객체를 first responder라고 했다.
event를 받기 위해서 Respnoder는 자신이 first responder가 될 수 있음을 나타내야 한다.
first responder가 될 수 있게 하려면 UIResponder의 subClass에서 canBecomeFirstResponder 프로퍼티를 override하여 true를 반환하도록 만들어야 한다.
Responder는 event message를 수신하는 것 이외에 target이 특정되지 않은 action message들을 받을 수도 있다.
(action message는 button이나 사용자가 조절 가능한 control과 같은 control들로부터 송신된다.)

그럼 우리가 위에서 본 그림과 상황에서는 first responder는 어떤 걸까?

사용자가 textField를 touch해 event가 발생했으므로 textFieldfirst responder가 될 것이다.

  • control은 연관된 target 객체와 직접 action message를 이용해 소통한다.
  • action message는 event는 아니지만 Responder Chain을 이용한다.
  • control의 target 객체가 nil일 경우 UIKit는 first responder에서 시작하여 적절한 action message를 구현한 객체를 만날 때까지 Responder Chain을 따라 이동한다.
  • view에 있는 Gesture Recognizer 또한 touch 등을 인식하지 못하면 UIKit는 view로 touch를 보내고 view도 touch를 처리하지 않을 경우 마찬가지로 Responder Chain을 따라 event를 보낸다.

어디서 event가 발생했는지 어떻게 아는가

UIKit는 어디서 touch event가 발생했는지 결정하기 위해 view 기반 hit-testing을 사용한다.
UIKit는 touch 위치를 view 계층에 있는 view 객체의 bound와 비교한다.
UIView의 hitTest(_:with:) Method는 특정 touch를 포함하는 가장 깊은 subView를 찾기 위해 view 계층을 따라서 이동한다.
이 가장 깊은 subView가 touch event의 first resopnder가 된다.

touch 위치가 view의 경계(bound) 밖이라면 hitTest(_:with:) Method는 해당 view와 그 view의 모든 subView들은 무시한다.
결과적으로 view의 clipToBounds 프로퍼티가 false라면 그 view의 밖에 있는 subView들은 touch를 포함하더라도 반환되지 않는다.

touch가 발생하면 UIKit는 UITouch 객체를 만들고 view와 연결한다.
touch 위치나 다른 파라미터들이 변경되면 UIKit는 같은 UITouch 객체를 새로운 정보로 업데이트한다.
이때 변경되지 않는 유일한 프로퍼티view이다.
touch 위치가 원래 view의 바깥으로 이동하더라도 touch의 view 프로퍼티는 변하지 않는다.
touch가 끝나면 UIKit는 UITouch 객체를 메모리에서 해제한다.


Altering the Responder Chain

Responder 객체의 next 프로퍼티를 override해 Responder Chain을 변경할 수 있다.
이때, next responder는 override한 프로퍼티에서 반환하는 객체이다.
많은 UIKit class들은 이 프로퍼티를 override하여 특정 객체들을 반환하고 있다.

UIView Object

  • 만약 해당 viewview controllerroot view라면 next responderview controller이다.
  • 아닌 경우에는 next responder는 해당 view의 super view이다.

UIViewController Object

  • 만약 view controllerviewwindowroot view라면 next responder는 window object이다.
  • 만약 view controller가 다른 view controller에 의해 present된 경우 next responderpresenting view controller이다.

UIWindow Object

  • window의 next responderUIApplication Object이다.

UIApplication Object

  • UIApplication Objectnext responderapp delegate이다.
  • 하지만 app delegateUIResponder의 instance면서 view, view controller 또는 App Object 자신이 아닐때만 해당된다.

0개의 댓글