iOS:事件处理机制(二)--事件传递,Responder Chain响应链

      Event Delivery: The Responder Chain

         当你设计你的程序的时候,大部分情况你应该动态回应事件。例如,当屏幕上某个对象上有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 theactive app’s event queue. For touch events, that object is a set of touches packaged in aUIEvent object. For motion events, the event object varies depending on which framework you use and what type of motion event you are interested in.

         An event travels along a specific path until it is delivered to an object that can handle it. First, thesingleton 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 keywindow object, which passes the event to an initial object for handling.The initial object depends on the type of event:

  •  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 thehit-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>.
  • Motion and remote control events. With these events, the window object sends the shaking-motion or remote control event to thefirst responder for handling. The first responder is described in<The Responder Chain Is Made Up of Responder Objects>.
         事件路径的最终目的是为了确定响应事件的对象。因此,UIKit 首先将事件发送给最适合处理事件的对象。对应点击事件(touch event),该对象是 hit-test view;对于其他事件,该对象是 first responder。接下来详细讲解如何确定hit-test view 和first responder。

         Hit-Testing Returns the View Where a Touch Occurred

          iOS使用hit-test view去寻找响应touch的view。简单来说就是你触发事件所在的那个View,寻找hit-test view的过程就叫做Hit-Testing。那么,系统是如何来执行Hit-Testing呢?

          例子:首先假设现在有如下这么一个UI布局,一种有ABCDE五个View,假设一个单击事件发生在了View D里面,系统首先会从最顶层的View A开始寻找,发现事件是在View A或者其子类里面,那么接着从B和C找,发现事件是在C或者其子类里面,那么接着到C里面找,这时发现事件是在D里面,并且D已经没有子类了,那么hit-test view就是View D啦。       

iOS:事件处理机制(二)--事件传递,Responder Chain响应链_第1张图片

            hitTest:withEvent: 方法详解

           介绍:遍历视图层次结构(traverses the view hierarchy),去寻找接受touch事件的视图。by calling thepointInside:withEvent: method of each subview to determine which subview should receive a touch event. IfpointInside:withEvent: returns YES, then the subview’s hierarchy is similarly traversed until the frontmost view containing the specified point is found. If a view does not contain the point, its branch of the view hierarchy is ignored.You rarely need to call this method yourself, but you might override it to hide touch events from subviews。

          详细执行过程

  • calls pointInside:withEvent:of self
  • If the return is NO, hitTest:withEvent: returns nil . the end of the story.
  • If the return is YES, it sends hitTest:withEvent: messages to its subviews. it starts from the top-level subview, and continues to other views until a subview returns a non- nil object, or all subviews receive the message.
  • If a subview returns a non- nil object in the first time, the first hitTest:withEvent: returns that object. the end of the story.
  • If no subview returns a non- nil object, the first hitTest:withEvent: returns self;
        重写hitTest:withEvent方法示范(pointInside:withEvent 方法内部实现未做判断,都认为点击在视图里面):
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    
    if ([self pointInside:point withEvent:event]) {        
        for (UIView *view in [self subviews]) {
            UIView *result = [view hitTest:point withEvent:event];
            if (result != nil) {
                return result;                
            }
        }        
        return self;        
    }else{        
        return nil;
    }    
} 
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{ 
    return YES;
}

说明:

1.该方法不起作用的情况(相当返回nil):userInteractionEnabled = NO 、hidden = YES 、alpha<0.01 (跟视图内容无关,例如内容透明等)

2.hit-test view只是快速找到第一响应者,首先得到了处理a touch event的机会,If the hit-test view cannot handle an event(或者hit-test没有找到第一响应者), 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

        hitTest:withEvent应用

       (一):父视图中有布局重叠的且都可响应用户操作的对象,如:ScrollView and Button,如果Button在ScrollView下面,正常情况下Button是不会成为第一响应者的,现在想让Button可以响应在其布局内的触摸事件。
       (二): UIView的子类不响应触摸事件,但其子View可以响应(navigation controller‘s view的效果 ),设置userInteractionEnabled=NO,使UIView和其子类都不会响应触摸事件);
       在网上一些应用中,都有调用[super hitTest:withEvent的方法???

    The Responder Chain Is Made Up of Responder Objects

       Many types of events rely on a responder chain for event delivery. Theresponder 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.
        A responder object is an object that can respond to and handle events. TheUIResponder 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 theUIApplication, UIViewController, 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.
        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.
2.Receiving a becomeFirstResponder message. If necessary, an object can send itself this message.

 // 注意: Make sure that your app has established its object graph before assigning an object to be the first responder. For example, you typically call the becomeFirstResponder 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.

          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.
  • Motion events. To handle shake-motion events with UIKit, the first responder must implement either the motionBegan:withEvent: or motionEnded:withEvent: method of the UIResponder class, as described in 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.
  • 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.
  • 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 and the sample code project, 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.
           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.

      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 thenext 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.
         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:事件处理机制(二)--事件传递,Responder Chain响应链_第2张图片


         

        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.
        4.The root view controller passes the event to the window object.
        5.The window passes the event to the app object.
//注意: 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

你可能感兴趣的:(iOS:事件处理机制(二)--事件传递,Responder Chain响应链)