了解事件的传递以及如何管理它们
Apps receive and handle events using responder objects. A responder object is any instance of the UIResponder class, and common subclasses include UIView , UIViewController、UIApplication.Responders receive the raw event data and must either handle the event or forward it to another responder object. When your app receives an event, UIKit automatically directs that event to the most appropriate responder object, known as the first responder. Unhandled events are passed from responder to responder in the active responder chain, which is a dynamic configuration of your app’s responder objects. There is no single responder chain within your app.
app用响应者对象接收和处理事件,响应者对象是UIResponder是任一实例,常用的子类包括UIView、UIViewController、UIApplication等。响应者对象接收最初的事件数据,首先去处理该事件,如果不能处理,就必须将之传递到下一个响应者对象那里。当app收到一个事件时,UIKit会自动地将这一事件分发给最适合的响应者对象那里,这个对象就成为第一响应者。没有被处理的事件会沿着活动的响应者链一个个对象进行传递,响应者链是app中响应者对象形成的一种动态结构。app中不存在单独的响应者链
UIKit defines default rules for how objects are passed from one responder to the next, but you can always change those rules by overriding the appropriate properties in your responder objects. Figure 1 shows the default responder chains in an app whose interface contains a label, a text field, a button, and two background views. If the text field does not handle an event, UIKit sends the event to the text field’s parent UIView object, followed by the root view of the window. From the root view, the responder chain diverts to the owning view controller before directing the event to the window. If the window does not handle the event, UIKit delivers the event to the UIApplication object, and possibly to the app delegate if that object is an instance of UIResponder and not already part of the responder chain.
UIKit定义了对象在响应者之间传递的规则,但你可以通过重写响应者对象合适的属性来改变这些规则。图1展示了默认情况下的响应者链,当前界面包含了一个label、一个textfield、一个按钮和两个背景view。如果textfield没有处理事件,UIKit 会把事件传递给textfield的父view对象,然后是window的根view。从根view开始,响应者链将事件转发给view所在的viewController,然后才会转给window。如果window也不能处理该事件,UIKit会将事件分发给UIApplication对象,或者给不在该响应者链中的其他UIResponder实例的代理进行处理(??)
寻找第一响应者
For every type of event, UIKit designates a first responder and sends the event to that object first. The first responder varies based on the type of event.
Touch events
The first responder is the view in which the touch occurred.
Press events
The first responder is the responder that has focus.
Shake-motion events
The first responder is the object that you (or UIKit) designate as the first responder.
Remote-control events
The first responder is the object that you (or UIKit) designate as the first responder.
Editing menu messages
The first responder is the object that you (or UIKit) designate as the first responder.
每种类型的事件,UIKit都会指派一个第一响应者并且优先将事件转发给它。第一响应者根据事件类型不同而不同
触摸事件:触摸所在的view
按压事件:获得焦点的view
摇动事件:开发者或UIKit指定的对象
远程控制事件:开发者或UIKit指定的对象
编辑菜单消息:开发者或UIKit指定的对象
注意:运动事件(motion events) 与加速计、陀螺仪和磁强计有关,该类事件不遵循响应链。相反,核心运动(Core Motion)会将这些事件直接传递给您指定的对象。具体可参考Core Motion Framework
控件接收到事件之后的处理
Controls communicate directly with their associated target object using action messages. When the user interacts with a control, the control calls the action method of its target object—in other words, it sends an action message to its target object. Action messages are not events, but they may still take advantage of the responder chain. When the target object of a control is
nil
, UIKit starts from the target object and walks the responder chain until it finds an object that implements the appropriate action method. For example, the UIKit editing menu uses this behavior to search for responder objects that implement methods with names likecut(_:) 、copy(_:), or paste(_:)
.
控件使用动作消息直接与和他们绑定的目标对象通信。当用户与控件交互时,控件会调用目标对象的action方法,换句话说,它会发送动作消息给目标对象。动作消息不是事件,但它们同样会利用响应者链。当一个控件的目标对象是nil时,UIKit会从目标对象开始沿着响应链寻找,直到找到了实现了action方法的对象。例如,UIKit编辑菜单使用该特性来寻找实现了cut(_:) 、copy(_:), or paste(_:)
方法的响应者对象。
If a view has an attached gesture recognizer, the gesture recognizer receives touch and press events before the view receives them. If all of the view’s gesture recognizers fail to recognize their gestures, the events are passed to the view for handling. If the view does not handle them, UIKit passes the events up the responder chain.
如果一个view已经被添加了手势识别器,那么手势识别器会在view之前优先接收到触摸或按压事件。如果该view的全部手势识别器都不能识别手势,事件才会被传递到view进行处理。如果view也不能处理,UIKite会将事件沿着响应链传递。
(所以,手势的优先级高于响应链哦)
确定哪个响应者包含触摸事件
UIKit uses view-based hit-testing to determine where touch events occur. Specifically, UIKit compares the touch location to the bounds of view objects in the view hierarchy. The
hitTest(_:with:)
method of UIView walks the view hierarchy, looking for the deepest subview that contains the specified touch. That view becomes the first responder for the touch event.
UIKit使用基于view的hittest方式来确定事件在哪里发生的。具体来说,UIKit将触摸位置与view对象进行逐级对比。UIView的hitTest(_:with:)
方法逐级执行,去寻找最深一级的包含特定触摸的子view。这个view就成了该触摸事件的第一响应者。
注意:如果一个事件的位置在view的bounds之外,hitTest(_:with:)
方法就会忽略该view及其所有subview。因此,当一个view的clipsToBounds
属性是false的时候,在该view之外的subview是不会被hitTest(_:with:)
返回的,即便事件的位置在该subview的界限之内
UIKit permanently assigns each touch to the view that contains it. UIKit creates each [
UITouch
] object when the touch first occurs, and it releases that touch object only after the touch ends. As the touch location or other parameters change, UIKit updates the [UITouch
] object with the new information. The only property that does not change is the containing view. Even when the touch location moves outside the original view, the value in the touch’s [view
] property does not change.
UIKit固定地将每个触摸分配给包含该触摸的view。当触摸事件发生时,UIKit会为每个事件创建UITouch对象,这个UITouch对象只会在触摸结束时才释放。当触摸的位置或其他属性变化时,UIKit会将新信息更新到该UITouch对象中。唯一不会改变的属性是其所属的view。即便触摸的位置移出原来的view,该触摸的view属性也不会改变。
改变响应者链
You can alter the responder chain by overriding the [
next
] property of your responder objects. When you do this, the next responder is the object that you return.
Many UIKit classes already override this property and return specific objects.
- [
UIView
] is the root view of a view controller, the next responder is the view controller; otherwise, the next responder is the view’s superview.- [
UIViewController
] objects.
- If the view controller’s view is the root view of a window, the next responder is the window object.
- If the view controller was presented by another view controller, the next responder is the presenting view controller.
- [
UIWindow
] objects. The window's next responder is the [UIApplication
] object.- [
UIApplication
] object. The next responder is the app delegate, but only if the app delegate is an instance of [UIResponder
] and is not a view, view controller, or the app object itself.
你可以通过重写响应者对象的next
属性来改变响应者链。当你这么做的时候,下一个响应对象就是你重写next
属性return的那个
许多UIKit class 已经重写了该属性且返回了特定的对象
- UIView对象,如果该view是viewController的root view,那么下一个响应者就是viewController;否则,下一个响应者就是它的superview。
- UIViewController对象
- 如果该viewController的view是一个window的root view,那么它的下一个响应者就是window
- 如果该viewController是被其他viewController所present出来的,那它的下一个响应者就是present它的那个viewController
- UIWindow对象,window的下一个响应者是UIApplication对象
- UIApplication对象,它的下一个响应者对象是app delegate。不过app delegate不能是UIResponder的实例,不能是view、viewController或者app对象自己