본문으로 바로가기

개요

앱은 UIResponder 타입의 객체를 이용해 여러가지 이벤트를 받고 처리한다

이 객채를 상속받은 객체로는 UIView, UIViewController, UIApplication등이 있고 이는 거의 대부분의 UI 객체들이 이벤트를 받고 처리할 수 있는 능력을 가진다는거다

 

Respopnder가 이벤트를 받으면 이를 처리하거나 다른 Responder가 처리할 수 있도록 넘겨준다

앱이 이벤트를 받으면 UIKit은 적절한 Responder를 지정해서 이벤트를 넘겨서 처리를 하게 되는데 처음으로 이벤트를 받는 Responder를 First Responder라 한다

 

만약 First Responder가 이벤트를 처리하지 못한다면 처리가능한 Responder가 나올 때 까지 연쇄적으로 다음 Responder에게 넘어가는데 이를 Responder Chain이라 한다

이 Chain은 뷰 계층의 상태에 따라 자동으로 구성되게 된다

UIWindow

UIWindow객체란 사용자에 가장 가까이 위치한 객체이다

사용자로부터 터치이벤트가 발생하면 이를 내부 객체로 전달하는 역할을 한다

HitTesting

HitTesting이란 First Responder를 찾는 과정이다

즉 터치 이벤트가 발생한 최상단 뷰를 찾는 행위라고 할 수 있다

터치이벤트가 발생한 지점의 최상단 뷰를 탐색하는데 역순으로 탐색한다

즉 이런 뷰가 있을고 yellowView를 터치했을때 hitTest는 yellowView가 아닌 UIWindow부터

UIWindow → UIView → greenView → yellowView 순으로 탐색을 할것이다

hitTest함수의 정의이다

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
	if !isUserInteractionEnabled || isHidden || alpha <= 0.01 {
		return nil
	}

	if self.point(inside: point, with: event) {
		for subview in subviews.reversed() {
			let convertedPoint = subview.convert(point, from: self)
		    if let hitTestView = subview.hitTest(convertedPoint, with: event) {
            return hitTestView
		    }
		}
		return self
	}
	return nil
}

위 코드는 애플의 공식 코드는 아니지만 어떤 똑똑한 사람이 hitTest는 이런식으로 동작할 것이다 하고 구현해둔 코드이다

  • 일단 hitTest는 userInteractionEnabled이 false 이거나 hidden상태 혹은 alpha가 0.01 미만인 뷰의 검사는 진행하지 않는다
  • point(insede:with) 함수는 현재 터치 이벤트가 발생한 지점이 UIWindow 내부인지 판단하여 내부라면 true를 반환하는 함수이다
  • 위 함수가 true를 발생했으나 subView가 없으면 해당 뷰가 first Responder가 되어 이벤트를 처리할 수 있다
  • 단 first Responder가 이벤트를 처리하지 못할 경우 다시 되돌아가면서 이벤트를 처리 가능한 responder를 찾는 과정을 진행한다 → 이를 Responder Chain이라 한다

💡 hitTest는 단순히 first Responder 를 찾는 과정이고 Responder Chain 은 이벤트를 처리 가능한 Responder를 찾는 과정이다 이 둘은 엄연히 다르다💡

 

다시 위 사진으로 돌아가서 puppleView를 터치한다고 생각해보자

greenView랑 겹친부분을 터치했을때랑 겹치지 않았을때랑 First Responder가 어떻게 다를까?

 

위 코드를 잘 보면 답이 나온다

hitTest를 타고가기 위해서는 터치한 지점에 subView가 있어야 한다

 

즉 greenView가 겹치는 puppleView를 터치했을때는 해당 터치지점 기준으로 mainView의 서브뷰로 greenView, greenView의 서브뷰로 puppleView가 있으므로 First Responder는 puppleView가 될것이다

 

하지만 greenView랑 겹치지 않은 부분의 puppleView를 터치했을때는 터치지점 기준 mainView의 subView로 어떤것도 존재하지 않기때문에 First Responder는 mainView가 될것이다

 

 

 

https://serious-hamburger-920.notion.site/Responder-ResponderChain-FirstResponder-a02d75bec20c4280947861f436c184ea