首页 > 代码库 > 【IOS笔记】Event Delivery: The Responder Chain

【IOS笔记】Event Delivery: The Responder Chain

Event Delivery: The Responder Chain  事件分发--响应链

When you design your app, it’s likely that you want to respond to events dynamically. For example, a touch can occur in many different objects onscreen, and you have to decide which object you want to respond to a given event and understand how that object receives the event.

当你设计应用程序时,你很可能想要动态地响应事件。 比如,一个触摸(touch)事件可以发生在屏幕上的不同对象中,并且对于一个给定事件你必须决定你想要响应哪个对象并理解哪个对象如何接收事件。

When a user-generated event occurs, UIKit creates an event object containing the information needed to process the event. Then it places the event object in the active app’s event queue. For touch events, that object is a set of touches packaged in a UIEvent object. For motion events, the event object varies depending on which framework you use and what type of motion event you are interested in.

当一个用户生成的事件发生时,UIKit创建一个事件对象,它包含了需要处理该事件的各种信息。 然后把事件对象放在活动应用程序的事件队列中。 对于多个触摸事件,对象是一组打包进一个UIEvent对象的触摸事件。 对于运动事件(motion events),事件对象根据你使用的不同框架以及你感兴趣的不同移动事件类型发生改变。 

An event travels along a specific path until it is delivered to an object that can handle it. First, the singleton UIApplication object takes an event from the top of the queue and dispatches it for handling. Typically, it sends the event to the app’s key window object, which passes the event to an initial object for handling. The initial object depends on the type of event.

事件沿着一个指定的路径传递直到它遇见可以处理它的对象。 首先一个UIApplication 对象从队列顶部获取一个事件并分发(dispatches)它以便处理。 通常,它把事件传递给应用程序的关键窗口对象,该对象把事件传递给一个初始对象来处理。 初始对象取决于事件的类型。

  • Touch events. For touch events, the window object first tries to deliver the event to the view where the touch occurred. That view is known as the hit-test view. The process of finding the hit-test view is called hit-testing, which is described in Hit-Testing Returns the View Where a Touch Occurred.                  触摸事件。 对于触摸事件,窗口对象首先尝试把事件传递给触摸发生的视图。那个视图被称为hit-test(点击测试)视图。 寻找hit-test视图的过程被称为hit-testing, 参见 “Hit-Testing Returns the View Where a Touch Occurred.”

  • Motion and remote control events. With these events, the window object sends the shaking-motion or remote control event to the first responder for handling. The first responder is described in The Responder Chain Is Made Up of Responder Objects.运动和远程控制事件。 对于这些事件,窗口对象把shaking-motion(摇晃运动)或远程控制事件传递给第一响应者来处理。第一响应者请参见 “The Responder Chain Is Made Up of Responder Objects.”

The ultimate goal of these event paths is to find an object that can handle and respond to an event. Therefore, UIKit first sends the event to the object that is best suited to handle the event. For touch events, that object is the hit-test view, and for other events, that object is the first responder. The following sections explain in more detail how the hit-test view and first responder objects are determined.

这些事件路径的最终目标是找到一个可以处理并相应事件的对象。 因此,UIKit首先把事件传递给最适合处理该事件的对象。 对于触摸事件,最适合处理该事件的对象是hit-test视图,而对于其它事件,那个对象是第一响应者。 以下章节讲述了更多关于如何决定hit-test视图和第一响应者的详情。

Hit-Testing Returns the View Where a Touch Occurred  Hit-Testing 返回触摸事件发生的视图

iOS uses hit-testing to find the view that is under a touch. Hit-testing involves checking whether a touch is within the bounds of any relevant view objects. If it is, it recursively checks all of that view’s subviews. The lowest view in the view hierarchy that contains the touch point becomes the hit-test view. After iOS determines the hit-test view, it passes the touch event to that view for handling.

iOS 使用hit-testing来找到事件发生的视图。 Hit-testing包括检查触摸事件是否发生在任何相关视图对象的范围内, 如果是,则递归地检查所有视图的子视图。在视图层次中的最底层视图,如果它包含了触摸点,那么它就是hit-test视图。等 iOS决定了hit-test视图之后,它把触摸事件传递给该视图以便处理。

To illustrate, suppose that the user touches view E in Figure 2-1. iOS finds the hit-test view by checking the subviews in this order:

假设用户触摸了图2-1中的视图E。iOS通过检查子视图来查找hit-test视图:

  1. The touch is within the bounds of view A, so it checks subviews B and C.因为触摸发生在视图A范围内,所以它检查子视图B和C。

  2. The touch is not within the bounds of view B, but it’s within the bounds of view C, so it checks subviews D and E.触摸不在视图B范围内,但是它在视图C范围内,所以它检查子视图D和E。

  3. The touch is not within the bounds of view D, but it’s within the bounds of view E.触摸没有在视图D范围内,但是它在视图E范围内。

    View E is the lowest view in the view hierarchy that contains the touch, so it becomes the hit-test view.视图E是视图层次结构的最底层并且它包含了触摸,因此它是hit-test视图。

Figure 2-1  Hit-testing returns the subview that was touched

技术分享

The hitTest:withEvent: method returns the hit test view for a given CGPoint and UIEvent. The hitTest:withEvent: method begins by calling thepointInside:withEvent: method on itself. If the point passed into hitTest:withEvent: is inside the bounds of the view, pointInside:withEvent:returns YES. Then, the method recursively calls hitTest:withEvent: on every subview that returns YES.

hitTest:withEvent: 方法为给定的CGPoint 和 UIEvent返回hit test 视图。hitTest:withEvent:方法通过在自身调用pointInside:withEvent: 方法开始。 如果传递到方法hitTest:withEvent:内的点在视图的范围内,pointInside:withEvent:返回YES。然后,方法递归地给每个子视图调用hitTest:withEvent:方法并返回YES。

If the point passed into hitTest:withEvent: is not inside the bounds of the view, the first call to the pointInside:withEvent: method returns NO, the point is ignored, and hitTest:withEvent: returns nil. If a subview returns NO, that whole branch of the view hierarchy is ignored, because if the touch did not occur in that subview, it also did not occur in any of that subview’s subviews. This means that any point in a subview that is outside of its superview can’t receive touch events because the touch point has to be within the bounds of the superview and the subview. This can occur if the subview’s clipsToBounds property is set to NO.

如果传递到hitTest:withEvent:方法的电不在视图的范围内,pointInside:withEvent:方法返回NO,点被忽视,并且hitTest:withEvent:返回nil. 如果一个子视图返回NO,则视图层次的整个分支都被忽视,因为如果触摸事件没有发生在那个子视图中,那么事件也不会在任何一个该子视图的子视图中发生。 这意味着在一个子视图中的任何点,如果它在其父视图的外面,那么它不能接收触摸事件,因为触摸点必须在父视图和子视图的范围内。但是如果子视图的clipsToBounds 特性被设置为NO时这可以发生。

Note: A touch object is associated with its hit-test view for its lifetime, even if the touch later moves outside the view.

注意:一个触摸对象在生命周期内都跟它的hit-test视图相关,即使触摸事件在稍候会移出该视图。

 

The hit-test view is given the first opportunity to handle a touch event. If the hit-test view cannot handle an event, the event travels up that view’s chain of responders as described in The Responder Chain Is Made Up of Responder Objects until the system finds an object that can handle it.

 hit-test是第一处理触摸事件的视图。如果hit-test视图不能处理该事件,事件沿着视图的响应链传递直到系统找到可以处理该事件的视图,响应链在 “The Responder Chain Is Made Up of Responder Objects”中有描述。

The Responder Chain Is Made Up of Responder Objects  响应链由响应者对象组成

Many types of events rely on a responder chain for event delivery. The responder chain is a series of linked responder objects. It starts with the first responder and ends with the application object. If the first responder cannot handle an event, it forwards the event to the next responder in the responder chain.

很多类型的事件都在事件传递中依赖响应链。 响应链是一系列相连的响应者对象。 它由第一个响应者开始,以应用对象结束。 如果第一响应者不能处理该事件,它把事件传递给响应链中的下一个响应者。

responder object is an object that can respond to and handle events. The UIResponder class is the base class for all responder objects, and it defines the programmatic interface not only for event handling but also for common responder behavior. Instances of the UIApplicationUIViewController, and UIView classes are responders, which means that all views and most key controller objects are responders. Note that Core Animation layers are not responders.

响应者对象是可以响应并处理各种事件的对象。 UIResponder 类是所有响应者对象的基类,它为事件处理和通用响应者行为都定义了可编程接口。 UIApplicationUIViewController, 和UIView的实例都是响应者,就是说所有的视图和大多数主要对象都是响应者。 请注意Core 动画层不是响应者。

The first responder is designated to receive events first. Typically, the first responder is a view object. An object becomes the first responder by doing two things:

第一响应者被设计为首先接收事件。通常,第一响应者是一个视图对象。 一个对象通过实现以下事情可以成为第一响应者:

  1. Overriding the canBecomeFirstResponder method to return YES. 重写 canBecomeFirstResponder 方法并返回YES。

  2. Receiving a becomeFirstResponder message. If necessary, an object can send itself this message.接收一个becomeFirstResponder 消息。如果必要,一个对象可以给自己发送该消息。

Note: Make sure that your app has established its object graph before assigning an object to be the first responder. For example, you typically call thebecomeFirstResponder method in an override of the viewDidAppear: method. If you try to assign the first responder in viewWillAppear:, your object graph is not yet established, so the becomeFirstResponder method returns NO.

注意:确保你的应用程序在把一个对象分配为第一响应者之前已经建立了它的对象图。比如,通常你重写viewDidAppear: 方法时会调用becomeFirstResponder方法。 如果你尝试在viewWillAppear:方法里分配第一响应者,那么你的对象图还没有建立,因此becomeFirstResponder方法返回NO。

 

Events are not the only objects that rely on the responder chain. The responder chain is used in all of the following:

事件并不是依赖响应链的唯一对象。响应链可以用于以下所有对象:

  • Touch events. If the hit-test view cannot handle a touch event, the event is passed up a chain of responders that starts with the hit-test view.触摸事件。如果hit-test视图无法处理一个触摸事件。该事件在以hit-test视图开始的响应链中往上传递。

  • Motion events. To handle shake-motion events with UIKit, the first responder must implement either the motionBegan:withEvent: ormotionEnded:withEvent: method of the UIResponder class, as described in Detecting Shake-Motion Events with UIEvent. 运动事件。 要想用UIKit处理摇晃运动事件,第一响应者必须实现UIResponder类的motionBegan:withEvent: 方法或motionEnded:withEvent: 方法,参见 “Detecting Shake-Motion Events with UIEvent.” 

  • Remote control events. To handle remote control events, the first responder must implement the remoteControlReceivedWithEvent: method of the UIResponder class.远程控制事件。 要想处理远程控制事件,第一响应者必须实现UIResponder类的 remoteControlReceivedWithEvent:方法。

  • Action messages. When the user manipulates a control, such as a button or switch, and the target for the action method is nil, the message is sent through a chain of responders starting with the control view. 动作消息。当用户操纵一个控件,比如一个按钮或开关,并且操作方法的目标(target)为nil时,消息通过以控件视图开始的响应者链里被发送。

  • Editing-menu messages. When a user taps the commands of the editing menu, iOS uses a responder chain to find an object that implements the necessary methods (such as cut:copy:, and paste:). For more information, see Displaying and Managing the Edit Menu in Text Programming Guide for iOS and the sample code project, CopyPasteTile.编辑菜单消息。 当用户点击了编辑菜单的命令时,iOS使用一个响应者链来找出实现必要方法的对象(比如cut:, copy:, 以及paste:) 。 更多信息,请看“Displaying and Managing the Edit Menu” 以及示例代码项目,CopyPasteTile.

  • Text editing. When a user taps a text field or a text view, that view automatically becomes the first responder. By default, the virtual keyboard appears and the text field or text view becomes the focus of editing. You can display a custom input view instead of the keyboard if it’s appropriate for your app. You can also add a custom input view to any responder object. For more information, see Custom Views for Data Input in Text Programming Guide for iOS.文本编辑。 当用户点击一个文本区或一个文本视图时,那视图自动成为第一响应者。 默认情况下,虚拟键盘出现后文本区或文本视图成为可编辑状态。 你可以显示一个自定义输入视图来取代键盘,如果它更适合你的应用程序。 你还可以添加一个自定义输入视图到任何响应者对象。 更多信息,请看 “Custom Views for Data Input”.

UIKit automatically sets the text field or text view that a user taps to be the first responder; Apps must explicitly set all other first responder objects with the becomeFirstResponder method.

UIKit 自动把用户点击的文本区或文本视图设置为第一响应者;应用程序必须明确地通过becomeFirstResponder方法设置所有其它第一响应者对象。

The Responder Chain Follows a Specific Delivery Path  响应者链遵循一个特定的传递路径

If the initial object—either the hit-test view or the first responder—doesn’t handle an event, UIKit passes the event to the next responder in the chain. Each responder decides whether it wants to handle the event or pass it along to its own next responder by calling the nextResponder method.This process continues until a responder object either handles the event or there are no more responders.

如果初始对象---hit-test视图或者第一响应者链--不能处理一个事件, UIKit 把事件传递给响应者链中的下一个响应者。 每个响应者决定是否想要处理该事件或者调用nextResponder 方法把该事件传递给它的下个响应者。 该过程一直持续直到找到一个响应者来处理该事件或者没有任何其它响应者。

The responder chain sequence begins when iOS detects an event and passes it to an initial object, which is typically a view. The initial view has the first opportunity to handle an event. Figure 2-2 shows two different event delivery paths for two app configurations. An app’s event delivery path depends on its specific construction, but all event delivery paths adhere to the same heuristics.

当iOS侦测到一个事件时,响应者链序列开始并把事件传递给一个初始对象,初始对象通常为一个视图。 初始视图首先可以处理一个事件。 图2-2显示了为两个应用程序配置的两个不同事件传递路径。 应用程序的事件传递路径依赖于它的特定结构, 但是所有的事件传递路径遵循同样的试探法(heuristics)。

Figure 2-2  The responder chain on iOS  图 2-2 iOS上的响应者链

技术分享

For the app on the left, the event follows this path:对于左边的应用程序,事件沿着以下路径:

  1. The initial view attempts to handle the event or message. If it can’t handle the event, it passes the event to its superview, because the initial view is not the top most view in its view controller’s view hierarchy.初始视图尝试处理事件或消息。 如果它不能处理该事件,它把事件传递给它的父视图,因为初始视图在它的视图控制器的视图层次里不是最顶层视图。 

  2. The superview attempts to handle the event. If the superview can’t handle the event, it passes the event to its superview, because it is still not the top most view in the view hierarchy.父视图尝试处理该事件。 如果父视图不能处理该事件,它把事件传递给父视图的父视图,因为该父视图还不是视图层次结构里的最顶层视图。

  3. The topmost view in the view controller’s view hierarchy attempts to handle the event. If the topmost view can’t handle the event, it passes the event to its view controller.视图控制器的视图层次结构中的最顶层视图尝试处理该事件。如果它还是不能处理该事件,它把事件传递给它的视图控制器。

  4. The view controller attempts to handle the event, and if it can’t, passes the event to the window.视图控制器尝试处理该事件,如果它不能处理,则把事件传递给窗口。

  5. If the window object can’t handle the event, it passes the event to the singleton app object.如果窗口还是不能处理该事件,它把事件传递给单个应用程序对象。

  6. If the app object can’t handle the event, it discards the event. 如果应用程序对象还是不能处理该事件,它把事件丢弃。

The app on the right follows a slightly different path, but all event delivery paths follow these heuristics:右边的应用程序沿着稍稍不同的路径,但是所有的事件传递路径遵循这些探索法:

  1. A view passes an event up its view controller’s view hierarchy until it reaches the topmost view. 一个视图在它的视图控制器的视图层次中把事件向上传递,直到它达到最顶层视图。

  2. The topmost view passes the event to its view controller.最顶层视图把事件传递给它的视图控制器。

  3. The view controller passes the event to its topmost view’s superview.

    Steps 1-3 repeat until the event reaches the root view controller.视图控制器把事件传递给它的最顶层视图的父视图。 重复步骤1-3 直到事件到达根视图控制器。

  4. The root view controller passes the event to the window object. 根视图控制器把事件传递给窗口对象。

  5. The window passes the event to the app object. 窗口把事件传递给应用程序对象。

Important: If you implement a custom view to handle remote control events, action messages, shake-motion events with UIKit, or editing-menu messages, don’t forward the event or message to nextResponder directly to send it up the responder chain. Instead, invoke the superclass implementation of the current event handling method and let UIKit handle the traversal of the responder chain for you.

 重要提示: 如果你实现了一个自定义视图用UIKit来处理远程控制事件,操作消息,摇晃运动事件,或者编辑菜单消息,不要直接把事件或消息分配给nextResponder并在响应者链里向上发送。 相反,调用父类的当前事件处理方法的实现方法并且让UIKit为你处理响应者链的遍历。

 

 

【IOS笔记】Event Delivery: The Responder Chain