https://onemoonstudio.tistory.com/8?category=845836 블로그 게시글 내용을 제게 맞게 정리해서 올린 글이라 자세한 내용은 이 블로그 주소로 들어가면 되겠습니당
"이벤트에 반응하고 다룰 수 있도록 하는 추상 인터페이스"
이것의 인스턴스를 Responder Object
라고 부릅니다. UIKit app
에서 이벤트를 다루는 근간으로 이루어져있습니다. UIApplication, UIViewController, UIView
같은 핵심 객체들 또한 리스폰더 입니다.
이벤트가 발생시 UIKit
은 responder object
로 이벤트를 전달합니다.
특정한 이벤트를 다루기위해 상응하는 메서드를 override
해야하고, 예시로 터치를 다루기 위해서는 touchesBegan, touchesEnded
등을 재정의 해야 합니다.
이벤트를 다루는 것 외에도, 리스폰더는 다루지 못한 이벤트를 앱의 다른 파트로 넘길 수 있습니다. 들어온 이벤트를 처리하지 않는다면, responder chain
을 따라서 다음으로 넘깁니다.
UIKit
은 리스폰더 체인을 동적으로 관리하는데, 사전에 정의된 룰에 따라서 어떤 오브젝트가 next
가 되어서 이벤트를 받을지 결정 합니다. 예로, view
는 superview
로, rootview
는 vc
로 진행 됩니다.
기본적으로 리스폰더는 UIEvent
를 처리하지만, input view
를 통해 커스텀 인풋을 받을 수도 있습니다. 예로, UITextField
를 유저가 누르는 경우, first responder
가 되면서 inputview(시스템 키보드)
를 보여줍니다. 이와 비슷하게 custom view
를 직접 만들어, 리스폰더가 active
되었을 때 보여주는 식으로도 가능하다.
"앱에서 하나의 유저 인터랙션을 설명하는 객체"
앱은 터치, 모션, 리모트 컨트롤 같은 여러타입에 이벤트를 받습니다. 이러한 이벤트는 타입과 서브 타입의 프로퍼티를 통해서 타입을 결정할 수 있습니다.
실제로 타입에는 터치, 모션, 리모트 컨트롤, 프레스 등이 있고, 서브타입에는 모션, 리모트 컨트롤의 세분화 되어 분류된것을 확인할 수 있습니다.
터치 이벤트는 해당 이벤트와 관련된 터치를 하나 이상 가지고 있고, 각 터치는 UITouchObject
입니다. 이 이벤트가 발생시, 시스템은 적절한 리스폰더를 찾아서 적절한 메소드를 실행시킵니다.
멀티 터치 관계에서는 UIKit
이 동일한 UIEvent
객체의 터치데이터를 업데이트 하면서 재사용합니다.
해당 객체를 가지고 있어서는 안되고, 만약에 갖고 있어야 한다면, 값을 복사하여 들고 있어야 합니다.
UIEvent
란 여러개의 터치를 분석하거나, 유저 인터랙션의 대해서 타입을 분류해 전달 되는 것이라 볼 수 있습니다.
"유저 인터랙션을 통해 특별한 액션과 의도를 갖춘 컨트롤을 관리하는 클래스"
UIButton, UISlider
를 포함 이 때 컨트롤은 target-action mechanism
을 이용하여 앱과 상호작용 합니다.
예로, 슬라이더의 경우, 유저가 드래그를 통해 값을 변경하는 UX
를 정리해 놓은 것이라고 볼 수 있고, UI
를 통해 구현한 것이 UIControl
이라고 설명이 가능하다.
만약 커스텀 이벤트 컨트롤이 필요한 경우에는 이를 서브클래싱 하는 것이 좋다. 확장이 필요시, 이미 존재하는 control class
를 상속받자.
Control
s state`를 통해서 컨트롤의 외관과 유저 인터랙션을 지원하는 것이 바뀐다. 이 상태에 따라서 컨트롤은 여러 상태중 하나의 상태를 가진다.
"앱을 컨트롤하기 위한 코드를 단순화 시켜준다"
터치이벤트를 쫓아다니면서 코드를 작성하는 대신, control-specific events(TouchUpInside 같은 것들)
에 대해서 action method
만 정리하면 된다.
액션 메소드를 컨트롤에 추가하기 위해서는, 액션 메소드, addTarget(_:action:for:)
메소드를 정의한 객체를 명시해야 한다.
타겟 오브젝트는 어떠한 것도 가능하다. 하지만 전형적으로 컨트롤을 포함하는 뷰컨트롤러의 루트뷰가 된다. 타겟 오브젝트를 nil
로 설정한다면, 컨트롤은 리스폰더 체인을 따라서 액션 메소드를 정의한 객체를 찾아 나선다.
예시로 하나 보자면
@IBAction func doSomething(sender: UIButton, forEvent event: UIEvent) {}
위 코드에서 sender
는 액션 메소드를 호출한 control
이고, event
는 control-related
를 발생 시키는 이벤트가 된다.
시스템은 컨트롤이 유저와 특정한 방식으로 인터랙션을 할 때 액션 메소드를 발생시킨다.
UIControl
이란 특정한 유저 인터랙션을 도와주는 UIView
이다
앱은 리스폰더를 통해서 이벤트를 받고 처리한다. 리스폰더는 UIResponder class
의 어떠한 인스턴스도 될 수 있고, UIView, UIViewController, UIApplication
이 그 예시이다.
리스폰더는 row event data
를 받게 되면 반드시 이를 처리하거나 다른 리스폰더로 전달해야만 한다.
이벤트를 받으면 UIKit
은 자동으로 가장 적절한 리스폰더를 찾아 전달하는데 이를 First Responder
라고 한다.
이벤트 전달 기본 흐름으로, view -> superview -> ... -> rootview -> vc -> window -> UIapplication -> Appdelegate
로 보면 된다.
UIkit
은 이벤트 유형에 따라, 이벤트의 First Responder
를 지정한다. (정해져 있는 듯 함)
accelerometers, gyroscopes, magnetometer 같은 모션 이벤트는 리스폰더 체인을 따르지 않는다. 대신 코어 모션이 특정한 객체로 이벤트를 전달한다.
UIControl
도 결국 UIView
의 서브 클래스로서, 리스폰더이며, 액션 메시지를 처리하기 위해서 리스폰더 체인을 사용하는 것으로 확인된다.
Gesture Recognizer
는 터치와 누르는 이벤트를 뷰보다 먼저 받는다. 만약 얘가 연속되는 터치 이벤트를 받지 못한다면, UIKit
은 이를 뷰로 전달한다.
만약 뷰가 터치를 처리하지 못한다면, UIKit
은 리스폰더 체인을 따라서 터치이벤트를 전달한다.
"UIKit은 터치 이벤트가 어디에서 발생 했는지를 확인하기 위해서 view-based hit-testing을 사용한다""
개인적으로 가장 이해가 잘되고 알고 싶었던 부분
UIKit
은 touch location
과 view hierarchy
에 있는 뷰 객체의 바운드를 비교한다.
UIView
의 hitTest(_:with:)
메소드는 뷰 계층(view hierarchy)을 순회하며 특정 터치를 포함하는 가장 깊은 서브뷰를 찾으며, 해당 뷰는 터치 이벤트의 First Responder
가 된다.
hitTest는 히든이거나, 비활성화거나, 투명도값이 0.01 미만이면 해당 뷰를 무시한다
만약 터치 이벤트가 뷰의 바운드 영역을 벗어나면, hitTest
는 해당 뷰 그리고 해당 뷰의 모든 서브뷰들을 무시한다. clipsToBounds
가 false
인 경우, 뷰 바운드 바깥에 있는 서브 뷰들은 터치를 포함하고 있더라도 리턴이 되지 않는다.
터치 이벤트가 발생시, UIKit
은 터치 객체를 만들어 뷰와 연결시킨다. 터치의 위치가 바뀌거나, 다른 파라미터가 변경된다면, UIKit
은 동일한 터치 객체를 새로운 정보로 업데이트 한다. 오로지 뷰만 변경되지 않는다 터치가 끝나면 UIKit
은 터치 객체를 릴리즈한다.
터치이벤트가 전달이 안되는 예로, 레이아웃이 깨진 경우를 찾아볼 수 있다. 터치로케이션이 뷰의 바운드 영역에서 벗어나게 되면 해당 뷰와 모든 서브뷰를 무시하므로, first responder
가 nil
이 되면서 터치 이벤트의 전달이 되지 않는 것이다.
리스폰더의 next
프로퍼티를 오버라이드해서, 리스폰더 체인을 변경할 수 있다. 이를 할 때, next responder
는 리턴하는 값이 된다.
예시로 살펴보자면
UIView
가 뷰 컨트롤러의 루트 뷰라면, next responder
는 뷰 컨트롤러가 된다. 루트 뷰가 아니라면 슈퍼 뷰가 된다.
UIViewController
의 뷰가 window
의 루트 뷰라면, next responder
는 window
가 된다. 만약 뷰 컨트롤러가 다른 뷰 컨트롤러에 의해서 띄워졌다면, next responder
는 presenting viewcontroller
가 된다.
UIWindow
에서 다음 응답자는 UIApplication
이다.
UIApplication
에서 딜리게이트가 UIResponder
의 인스턴스이고, view, vc, app
이 아닌 경우에만 다음 응답자가 appdelegate
가 된다.
리스폰더 체인은 리스폰더로 이루어진 연결리스트 이다. 리스폰더는
next
라는 프로퍼티가 존재. 이를 통해 다음 리스폰더를 찾을 수 있다.
UIEvent
생성UIKit
은 이 이벤트를 처리할 가장 적절한 Responder
를 찾음first responder
가 이벤트를 처리할 수도 있지만, 이벤트를 처리하지 못하는 경우 responder
의 특성에 따라 리스폰더 체인을 통해 이벤트를 처리 가능한 리스폰더를 찾는다. (처리 또는 무시)아직 이해가 잘 안되는 부분이 있어서 더 읽어보고, 찾아봐야 할둣