상황 설명 : 상위 뷰(superview)로 GreyView(customized)가 있고 이 뷰의 하위 뷰(subview)로 MyLabel, MyButton, MyTextField(역시 모두 cutomized)가 있다. 아래 코드처럼 responder chain의 responder가 될 수 있는 AppDelegate, ViewController, GreyView, MyLabel, MyButton에 모두 touchesBegan
을 재정의함.(MyTextField는 현재 상황과 관계 없으므로 제외함)
뷰들의 hierarchy
extension AppDelegate {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
print(#file, #line, #function, #column)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tabField(_ sender: UITapGestureRecognizer) {
print(#file, #line, #function, #column)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
print(#file, #line, #function, #column)
}
}
class GreyView: UIView {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
print(#file, #line, #function, #column)
}
}
class MyLabel: UILabel {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
print(#file, #line, #function, #column)
}
}
class MyButton: UIButton {
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
print(#file, #line, #function, #column)
}
}
실행결과 (콘솔 화면)
/Users/kimdo/workspace/ResponderSample/ResponderSample/View/MyButton.swift 15 touchesBegan(_:with:) 40
=> UIKit는 사용자가 터치를 하면 해당 event에 대한 first responder를 찾아 event를 전달한다. 이 경우 first responder는 MyButton 객체이고, 따라서 이때 MyButton 클래스에서 재정의한 touchesBegan이 호출된다. 함수 내부를 살펴보면 이때 super.touchesBegan이 호출이 된다. super.touchesBegan은 MyButton의 super class인 UIButton이 재정의한 touchesBegan으로, 해당 event에 대해 자신이 respond한다. 따라서 자신이 event를 처리했으니 해당 event를 responder chain에 따라 다음(next) responder로 전달하지 않을 것이다. 따라서 super.touchesBegan() 함수가 리턴되고 이후의 print(#file, #line, #function, #column)
이 호출되는 것이다.
실행결과 (콘솔 화면)
/Users/kimdo/workspace/ResponderSample/ResponderSample/AppDelegate.swift 14 touchesBegan(_:with:) 40 /Users/kimdo/workspace/ResponderSample/ResponderSample/ViewController.swift 23 touchesBegan(_:with:) 40 /Users/kimdo/workspace/ResponderSample/ResponderSample/View/GreyView.swift 15 touchesBegan(_:with:) 40 /Users/kimdo/workspace/ResponderSample/ResponderSample/View/MyLabel.swift 15 touchesBegan(_:with:) 40
=> 이 상황에서 first responder는 MyLabel 객체이고, 따라서 이때 MyLabel 클래스에서 재정의한 touchesBegan
이 호출된다. 함수 내부를 살펴보면 이때 super.touchesBegan
이 호출이 된다. super.touchesBegan
은 MyLabel의 super class인 UILabel이 재정의한 touchesBegan으로 해당 event에 대해 자신이 respond 하지 않는다. 따라서 해당 event는 responder chain의 다음(next) responder에게 전달될 것이다. 이때 다음의 뷰들도 해당 event를 처리하지 않으니 AppDelegate까지 event가 전달될 것이고 결국 아무도 처리하지 않고 event는 사라진다. 따라서 위의 결과처럼 출력이 연달아 되는 것이다.
여기서 문제. first responder인 MyLabel 객체가 먼저 event를 받는데 왜 responder chain의 끝에 있는 AppDelegate의
touchesBegan
의print()
문이 먼저 호출이 될까?
touchesBegan
이 먼저 호출이 되는 것 맞다. 하지만 함수를 내부를 잘 살펴보면 super.touchesBegan
도 호출이 된다. super.touchesBegan
은 블록 함수로, 그 아래 코드인 print문은 super.touchesBegan
이 끝날때까지 호출이 되지 않는다. 이때 super.touchesBegan
은 UILabel 클래스의 재정의한 touchesBegan으로 위에서 말했던 것처럼 해당 event에 대해 자신이 respond하지 않는다. 따라서 해당 event를 responder chain의 다음(next) 뷰로 전달한다.touchesBegan
을 호출할 것이고 super.touchesBegan
함수가 끝날 때까지 아래 코드인 print문은 호출하지 않는다.super.touchesBegan
이 호출이 끝나고 나서야 아래 코드인 print문이 호출되고 해당 함수가 종료되고, 그 다음엔 ViewController의 super.touchesBegan
이 호출이 끝나고 아래 코드인 print문인 호출되고 이하 하위 뷰들도 똑같은 순서로 print문을 호출한다.