Responder Chain Example

Doehyung Kim·2020년 3월 1일
1
post-custom-banner

1. Responder Chain Example

  • 상황 설명 : 상위 뷰(superview)로 GreyView(customized)가 있고 이 뷰의 하위 뷰(subview)로 MyLabel, MyButton, MyTextField(역시 모두 cutomized)가 있다. 아래 코드처럼 responder chain의 responder가 될 수 있는 AppDelegate, ViewController, GreyView, MyLabel, MyButton에 모두 touchesBegan을 재정의함.(MyTextField는 현재 상황과 관계 없으므로 제외함)

  • 뷰들의 hierarchy

스크린샷 2020-03-01 오전 11 50 07
  • 앱 화면 캡쳐
스크린샷 2020-03-01 오전 11 57 55
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)이 호출되는 것이다.

  • 반면에 MyLabel이 있는 곳에 터치를 한다면 무슨 일이 일어날까?

실행결과 (콘솔 화면)

/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는 사라진다. 따라서 위의 결과처럼 출력이 연달아 되는 것이다.

2. Question

여기서 문제. first responder인 MyLabel 객체가 먼저 event를 받는데 왜 responder chain의 끝에 있는 AppDelegate의 touchesBeganprint()문이 먼저 호출이 될까?

3. Answer

  • 답 : first responder인 MyLabel 객체의 재정의한 touchesBegan이 먼저 호출이 되는 것 맞다. 하지만 함수를 내부를 잘 살펴보면 super.touchesBegan도 호출이 된다. super.touchesBegan은 블록 함수로, 그 아래 코드인 print문은 super.touchesBegan이 끝날때까지 호출이 되지 않는다. 이때 super.touchesBegan은 UILabel 클래스의 재정의한 touchesBegan으로 위에서 말했던 것처럼 해당 event에 대해 자신이 respond하지 않는다. 따라서 해당 event를 responder chain의 다음(next) 뷰로 전달한다.
    이때 다음 뷰들인 GreyView, ViewController, AppDelegate한테 전달이 된다. 이 뷰들도 event를 받으면 마찬가지로 재정의한 touchesBegan을 호출할 것이고 super.touchesBegan함수가 끝날 때까지 아래 코드인 print문은 호출하지 않는다.

    따라서 마지막에 AppDelegate의 super.touchesBegan이 호출이 끝나고 나서야 아래 코드인 print문이 호출되고 해당 함수가 종료되고, 그 다음엔 ViewController의 super.touchesBegan이 호출이 끝나고 아래 코드인 print문인 호출되고 이하 하위 뷰들도 똑같은 순서로 print문을 호출한다.

    그래서 AppDelegate 객체의 print문이 MyLabel 객체의 print문보다 먼저 호출되는 것이다.

4. Resource

ResponderSample.zip

5. Reference

profile
해피 코딩
post-custom-banner

0개의 댓글