[Article] Responder Chain to Handle Events

Lena·2021년 9월 3일
1

Apple Developer Documentation


앱은 responder object를 통해 event를 받고 핸들링합니다. 여기서 responder object는 UIResponder의 인스턴스를 말하는데, UIView, UIViewController, UIApplication의 subsclass들이 일반적으로 responder object가 됩니다.

Responder object는 raw event data를 받고 처리하거나 다른 responder object에게 전달(forwarding) 해야 합니다. app이 event를 받으면 UIKit이 자동으로 적절한 responder object(first respnder)에게 전달합니다.

iOS → App(UIApplication) → (First) Responder object

처리되지 않은 이벤트는 유효한 responder chain 내에서 responder에서 responder로 전달됩니다.

which is the dynamic configuration of your app’s responder objects.

https://docs-assets.developer.apple.com/published/7c21d852b9/f17df5bc-d80b-4e17-81cf-4277b1e0f6e4.png

Figure 1 Responder chains in an app

다이어그램은 responder chain에 따라 하나의 responder에서 다른 responder로 어떻게 이벤트가 이동하는지를 나타냅니다.

  • text field가 이벤트를 처리하지 못하면, UIKit가 text field의 parent UIView 객체로 이벤트를 보냅니다. = background view
  • background view가 window의 root view이면 responder chain이 view가 속한 view controller에게로 전환됩니다(divert). view controller가 window에게 이벤트를 전달합니다.
  • window가 이벤트를 처리하지 못하면 UIKit는 이벤트를 UIApplication에 전달합니다.
  • 가능하다면, UIApplication은 UIApplicationDelegate에게 이벤트를 전달합니다.
    • UIApplicationDelegate가 UIResponder의 instance 이면서 responder chain이 일어나지 않았을 경우에만

Determining an Event's First Responder

UIKit는 이벤트 타입에 따라 first responder object를 지정합니다.

  • touch event — 터치를 받은 view
  • press event — focus된 object
  • shake motion event — 개발자 (or UIKit)가 지정한 object
  • remote control event — 개발자 (or UIKit)가 지정한 object
  • editing menu message — 개발자 (or UIKit)가 지정한 object

accelerometers, gyroscopes, magnetometer와 관련된 motion event들은 responder chain를 따르지 않습니다. 대신 Core Motion이 이벤트를 지정된 object에게 directly 전달합니다.

accelerometers : shake
gyroscopes : 회전
magnetometer : ?

Controls은 관련된 target object와 action message를 사용해서 직접적으로 커뮤니케이션 합니다. (target- action)

user가 control을 사용해서 상호작용하면 control은 action message를 target object에 보냅니다.

action message는 event가 아니지만, responder chain을 이용합니다.

target object가 nil 이면, UIKit는 action method를 구현한 객체부터 responder chain으로 target을 탐색(traverse)합니다.

UIControl은 action message를 전달할 target을 찾기 위해 responder chain을 이용하기도 한다.

예를 들면, UIKit editing menu가 cut, copy, paste 와 같은 동작을 구현할 responder object를 찾기 위해 responder chain이 일어납니다.

Gesture recognizer가 view보다 먼저 touch and press 이벤트를 받습니다. 뷰의 gesture recognizer가 이벤트를 인식하는데 실패하면 UIKit가 이벤트를 뷰에게 보냅니다.

view가 이벤트를 처리하지 못하면, UIKit는 responder chain에 따라 전달합니다.

Determining Which Responder Contained a Touch Event

UIKit은 view를 기반으로 한 hit-testing으로 touch event가 발생한 뷰를 결정합니다. 특히 view 계층 구조 내 view 객체의 가장자리를 비교해서 결정합니다. hitTest(_:with:) 메소드는 뷰의 hierarchy를 돌면서 가장 깊은 subview를 찾고, 그 객체가 touch event를 받을 first responder가 됩니다.

view를 기반으로 한 hit-testing으로 touch event를 처리할 뷰를 결정한다.
hit-testing은 UIView 안에 정의된 hitTest(_:with:) 를 통해 이루어진다.

touch가 발생하면, UIKit은 UITouch object를 생성하고, 뷰와 연결시킵니다. touch 위치 또는 다른 parameters 변화에 따라 UIKit는 동일한 UITouch object를 새로운 정보와 함께 업데이트합니다. 하지만 touch 객체의 view property는 변하지 않습니다. touch가 끝나면 UIKit이 UITouch object를 release 합니다.

touch event가 발생하면 UIKit가 UITouch 객체를 생성해서 (hit-testing을 통해 찾은) view와 연결한다.
touch 객체의 view property에 assign. touch 위치가 변하더라도, view 속성 값은 변하지 않는다.
touch가 끝나면(touchesEnded()가 호출되면) UIKit가 touch object를 release(메모리에서 해제) 한다.

// UIResponder
func touchesBegan()
func touchesMoved()
func touchesEnded()
func touchesCancelled() // 전화, 문자 등으로 터치 이벤트가 interrupte 된 경우

Altering the Responder Chain

responder object의 next property를 오버라이딩해서 responder chain을 변경할 수 있습니다.

이미 많은 UIKit class들이 next property를 오버라이딩해서 특정 객체를 리턴하고 있습니다. 가령:

  • UIView : view가 view controller의 root view라면, next responder는 view controller가 됩니다. 그렇지 않은 경우, next responder는 view의 super view가 됩니다.
  • UIViewController : view controller가 window의 root view라면, next responder는 window 객체가 됩니다.
  • view controller가 다른 view controller에 의해 present 된 경우에는, next responder는 presenting view controller가 됩니다.
  • UIWindow : window 객체의 next responder는 UIApplication 객체입니다.
  • UIApplication : next responder는 app delegate가 됩니다. 단, app delegate가 UIResponder의 인스턴스이면서 view, view controller, app 객체 자체가 아닌 경우에만 해당됩니다.
@available(iOS 2.0, *)
open class UIResponder : NSObject, UIResponderStandardEditActions {

  open var next: UIResponder? { get }
  
  open var canBecomeFirstResponder: Bool { get } // default is NO
  open func becomeFirstResponder() -> Bool

  open var canResignFirstResponder: Bool { get } // default is YES
  open func resignFirstResponder() -> Bool

  open var isFirstResponder: Bool { get }
  // ...
}

0개의 댓글