이벤트에 응답하고 처리하기 위한 추상 인터페이스
Responder 객체 → UIKit 앱의 이벤트 처리를 담당
UIApplication, UIViewController 및 UIView를 상속하는 모든 객체들은 UIResponder를 상속한다.
이벤트가 발생하면 UIKit은 앱의 responder 객체로 이벤트를 전달하게 된다.
-> 이벤트 전달에 대한 자세한 내용은 responder chain을 학습할 때 알아보기로 한다.
@available(iOS 2.0, *)
@MainActor open class UIResponder : NSObject, UIResponderStandardEditActions {
...
open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
...
}
responder 객체가 터치 이벤트를 처리하려면 다음 메서드들을 재정의해야 한다.
responder 객체가 이벤트를 받게 되면 재정의 해두었던 메서드들을 실행하는 방식으로 진행된다.
UIResponder 클래스는 터치 이벤트 외에도 모션 이벤트, 리모컨 이벤트, 프레스 이벤트 등 여러 종류의 이벤트를 처리하기 위한 메서드들을 정의하고 있다.
responder 객체는 자신이 처리할 수 없는 이벤트가 있으면 이를 responder chain의 다음 객체로 전달한다.
UIKit은 어떤 객체가 이벤트를 수신해야 하는지 결정하기 위해 특정 규칙을 사용하여 responder chain을 구성하고 이를 동적으로 관리한다.
responder 객체는 UIEvent 객체를 처리하지만 input view를 통해 custom input에 접근할 수도 있다.
키보드가 대표적인 input view의 예시인데, 사용자가 UITextField
나 UITextView
를 탭하면 View는 first responder가 되어 키보드인 input view를 표시하게 된다.
(그래서 키보드를 내리려면 resignFirstResponder
메서드를 사용함.)
마찬가지로 custom input view를 만들고 다른 responder 객체가 활성화되어 있을 때 이를 표시할 수 있다.
custom input view를 responder 객체와 연동하려면 해당 view를 responder 객체의 inputView
프로퍼티로 할당하면 된다.
이벤트 처리와 응답 과정에서 객체들 간의 상호작용을 관리하는 개념
responder 객체가 이벤트 데이터를 수신하면 이를 처리하거나 다른 responder 객체에 넘겨야 한다.
아래의 이미지는 label, text field, button 그리고 두 개의 background view로 구성된 앱에서의 responder를 나타낸다.
다음과 같이 inputView를 커스텀하게 만들어 사용할 수도 있다.
(이미지 출처: https://apps.apple.com/kr/app/디자인키보드-폰트와-테마가-있는-키보드/id1460767601)
해당 다이어그램은 responder chain에 따라 이벤트가 어떻게 이동하는지를 보여준다.
이벤트가 처리되는 방식은 다음과 같다.
UIKit은 이벤트 타입에 따라 first responder를 지정한다.
터치 이벤트 | 터치가 일어나는 화면 |
---|---|
프레스 이벤트 | 초점이 맞춰진 객체 |
모션 이벤트 | 개발자 혹은 UIKit이 지정한 객체 |
리모트 컨트롤 이벤트 | 개발자 혹은 UIKit이 지정한 객체 |
Editing menu 메시지 | 개발자 혹은 UIKit이 지정한 객체 |
컨트롤은 action message를 사용하여 연관이 있는 대상 객체와 직접적으로 통신한다. 사용자가 컨트롤과 상호 작용할 때 컨트롤은 대상 객체에 action message를 보낸다.
action message는 이벤트가 아니지만 responder chain을 사용할 수 있다. 컨트롤의 대상 객체가 nil이면 UIKit은 대상 객체에서 시작하여 적절한 action 메서드를 구현하는 객체를 찾을 때까지 responder chain을 순회한다.
예를 들어 UIKit editing menu는 cut:
, copy:
또는 paste:
와 같은 이름을 가진 메서드를 구현하는 responder 객체를 찾는다.
제스쳐 인식기는 view가 받기 전에 터치 및 프레스 이벤트를 수신한다. view의 제스쳐 인식기가 이벤트를 인식하지 못하면 UIKit이 이를 view로 보낸다. view가 이벤트를 처리하지 않으면 UIKit은 responder chain 상에서 view 위로 이벤트를 전달한다.
UIKit은 view 기반의 hit test를 사용하여 터치 이벤트가 발생하는 위치를 결정한다. 이때 UIKit은 터치 위치를 view hierarchy에서 view 객체가 차지하는 bounds와 비교한다. UIView의 hitTest:withEvent:
메서드는 view hierarchy를 순회하여 터치 이벤트에 대한 first responder가 되는 해당 터치를 포함하는 subview를 찾는다.
터치 이벤트가 발생하면 UIKit은 UITouch 객체를 생성하고 이를 view와 연결한다. 터치 위치 혹은 파라미터가 변경되면 UIKit은 동일한 UITouch 객체를 새로운 정보로 업데이트한다. 변경되지 않은 유일한 속성은 view이다. (터치 위치가 원래 view 밖으로 이동하더라도 터치의 view 프로퍼티 값은 바뀌지 않는다.) 터치가 끝나면 UITouch 객체를 해제한다.
responder 객체의 nextResponder
프로퍼티를 재정의하여 responder chain을 변경할 수 있다. nextResponder
프로퍼티로 지정된 객체가 다음 responder가 된다.
많은 UIKit 클래스가 다음과 같이 이 프로퍼티를 재정의하고 있다.