I. Classification of events
For iOS device users, there are three main ways to manipulate devices: Touch the screen, shake the device, and control the device through a remote controlled facility. The following three types of events are available:
1. Touchscreen events (Touch event)
2, motion events (motion event)
3. Remote Control Events (Remote-control event)
Today, as an example of touch event, the process of handling events in the Cocoa Touch framework is illustrated. First we have to introduce the concept of the responder chain:
Second, the responder chain (Responder Chain)
First, the responder object, Responder object, has the ability to respond to and handle events.
A responder chain is a hierarchical structure of a series of responder objects.
Uiresponder is the base class for all response objects, and the interfaces that handle each of these events are defined in the Uiresponder class. The familiar uiapplication, Uiviewcontroller, UIWindow, and all Uikit classes that inherit from UIView are inherited directly or indirectly from Uiresponder, so their instances are the responder objects that can form the responder chain. Figure one shows the basic composition of the responder chain:
Figure A
As can be seen from figure I, the responder chain has the following characteristics:
1, the responder chain is usually composed of a view (UIView);
2. The next responder of a view is its view controller (Uiviewcontroller), if any, and then to its parent view (Super view);
3. The View controller (if any) the next responder is the parent view of the view it manages;
4. The content view of the Singleton window (UIWindow) will point to the window itself as its next responder
It should be noted that the Cocoa touch app is not like the Cocoa application, it has only one UIWindow object, so the entire responder chain is a little simpler;
5. The singleton application (uiapplication) is the end point of a responder chain, and its next responder points to nil to end the entire loop.
Iii. Distribution of events (event Delivery)
First responder refers to the current responder object (usually a UIView object) that accepts the touch, meaning that the object is currently interacting with the user, which is the beginning of the responder chain. The mission of the entire responder chain and event distribution is to find the first responder.
The UIWindow object sends the event as a message to the first responder, giving it an opportunity to handle the event first. If the first responder does not process, the system passes the event (through the message) to the next responder in the responder chain to see if it can be processed.
When the iOS system detects a finger touch, it packs it into a Uievent object and puts it into the event queue of the currently active application. The uiapplication of a singleton takes a touch event from the event queue and passes it to the uiwindow of the Singleton, UIWindow object first uses the Hittest:withevent: method to find the view that contains the initial point of the touch operation, That is, you need to pass the touch event to its processed view, which is called Hit-test view.
The UIWindow instance object first calls Hittest:withevent on its content view: This method calls Pointinside:withevent on each view in its view hierarchy: (This method is used to determine if the location of the click event is in the current view, to see if the user clicked the current view), and if pointinside:withevent: return yes, continue to cascade until you find where the touch operation occurred, This is the Hit-test view you are looking for.
Hittest:withevent: The process of the method is as follows:
First call the current view's Pointinside:withevent: method to determine whether the touch point is within the current view;
If no is returned, hittest:withevent: returns nil;
If yes, the Hittest:withevent: message is sent to all the child views of the current view (Subviews), and all child views are traversed in the order from the topmost view to the bottom view, which is the forward traversal from the end of the subviews array. Until a child view returns a non-empty object or all the child views are traversed;
If the child view returns a non-empty object for the first time, the Hittest:withevent: method returns this object, processing ends;
If all child views return non, the Hittest:withevent: method returns itself (self).
Figure II
Join the user click on the view E, the following together with the Diagram II Hit-test view of the process:
1, A is the root view of UIWindow, therefore, the Uiwindwo object will be the prime minister of a hit-test;
2, obviously the user clicks the scope is within the range of a, therefore, pointinside:withevent: Returns the Yes, then will continue to check A's child view;
3. At this time there will be two branches, B and C:
The clicked range is no longer within B, so the pointinside:withevent of the B branch: Return no, corresponding to the hittest:withevent: return nil;
Click on the range in C, that is, C pointinside:withevent: return yes;
4. There are two branches of D and E:
The clicked Range is no longer in D, so the pointinside:withevent of D: Return no, corresponding to the hittest:withevent: return nil;
The clicked Range is within E, that is, E's pointinside:withevent: Returns Yes, since E has no child view (which can also be understood as hit-test when the sub-view of E is returned to nil), therefore, E's hittest:withevent: will return E, Back again, that is the hittest:withevent of C: return e--->>a hittest:withevent: return E.
At this point, the first responder of this click event is successfully found through the event distribution logic of the responder chain.
It is not difficult to see that this process is a bit like the idea of binary search, so that the fastest speed, the most accurate positioning can respond to touch events UIView.
Third, the description
1. If the final hit-test does not find the first responder, or if the first responder does not handle the event, the event will go up backwards along the responder chain, and the event will be discarded if neither the UIWindow instance nor the UIApplication instance can handle the event;
2. Hittest:withevent: The method ignores the view that hides (Hidden=yes), disables user action (Userinteractionenabled=yes), and the alpha level is less than 0.01 (alpha< 0.01) of the view. If the area of a child view exceeds the bound area of the parent view (the Clipstobounds property of the parent view is no, so that the child view content of the parent view's bound area is also displayed), the touch operations of the child view outside the parent view will normally not be recognized. Because the parent view's Pointinside:withevent: Method returns no, it does not continue to traverse the child view down. Of course, you can also override the Pointinside:withevent: method to handle this situation.
3, we can rewrite hittest:withevent: to achieve certain purposes, the following link is an interesting application example, of course, in practical applications rarely used.
http://download.csdn.net/detail/wzzvictory_tjsd/5716299
We often use UIView to write our own uitouchevent. For example, the following 3 methods are implemented directly in a view/viewcontroller:
-(voidTouchesbegan:(Nsset *)TouchesWithevent:(uievent *)event{}-(void) Touchesmoved:(Nsset *) touches withevent :(uievent *) event{}-(void) Touchesended:(Nsset *) touches withevent :(uievent *) event{}-(void) Touchescancelled:(Nsset *) touches withevent :(uievent *) event{}
We use a lot of it, but do you know who is the example of these 4 methods? If you say it is uiview, then why we can use it in the Uiviewcontroller, they are not the inheritance relationship.
Note that these 4 instance methods come from the common parent class of UIView and Uiviewcontroller: Uiresponder. It is the protagonist of our day.
Basically all the graphical interfaces we can see are inherited from Uiresponder, so, why is it sacred?
Uiresponder, the so-called parent of many views, is in charge of the user's Operation event distribution. Without him, how does our capacitive screen respond to the user's actions by passing them to our view?
Let's first look at the concept of the responder chain in iOS:
Each application has a responder chain, our view structure is an n-fork tree (a view can have multiple sub-views, a child view has only one parent view at a time), and every object that inherits Uiresponder can play a node in this N-fork tree. When the leaf node becomes the highest responder, starting from this leaf node to its parent node begins to chase a chain, then for this one leaf node, this chain is the current responder chain. The responder chain distributes the uievent captured by the system to the Uitouch from the leaf node, where you can choose to stop distribution, or you can choose to continue distributing downward.
Example:
I created a new project with the Singleview template, which has only one uiviewcontroller on the main window and a button on the view. This is the structure of the N-fork Tree of all uiresponder subclasses in this project:
Then he does not look like the N-fork tree, but does not mean that the person is not a n-fork tree, when our project is complex, can this view have multiple UIButton nodes? So he is a tree.
In fact, we have to write this tree complete, should also be counted on the UIButton Uilabel and Uiimageview, because they are also uireponder subclasses. This is not the first consideration here.
For UIButton, if he is a leaf node at this point, then we are responding to his response chain, and his former responder should be the view of our controller (the leaf nodes in the tree will always be more preferentially distributed than the parent node, but it does not mean that he can respond first in time, Let's talk about why below). So we tried to print the button's Nextreponder object anywhere. The Nextresponder object is an instance method of the Uireponder class that returns the previous responder instance of any object in the tree:
NSLog (@ "%@", _testbutton.nextresponder);
Console output message:
2013-09-21 03:40:25.989 Response Chain [614:60b] <UIView:0x16555e10; frame = (0 0; 568); autoresize = rm+bm; layer = <calayer:0x16555e70>>< /c4>
We can tell by the size of this uiview that he is the UIView in our only controller.
Next we print out who the next responder of this uiview is:
NSLog (@ "%@", _testbutton.nextresponder.nextresponder);
Output:
2013-09-21 03:45:03.914 Response Chain [621:60b] <RSViewController:0x15da0e30>
Look, then add a nextresponder:
2013-09-21 03:50:49.428 Response Chain [669:60b] (NULL)
Why is there no father here Viewcontroller?
Note that this code is written in Viewdidload, and we know the life cycle of this method is relatively early, so we can write it somewhere else or delay it for a while, both ways to get the results ( It can be inferred that the construction of our responder tree is done in the Viewdidload cycle, which merges the responder subtree of the current instance into our entire root tree:
2013-09-21 03:53:47.304 Response Chain [681:60b] <UIWindow:0x14e24200; frame = (0 0; 568); gesturerecognizers = <NSArray:0x14e242e0>; layer = <UIWindowLayer:0x14e244a0>>
and continue on to the chase:
2.0; dispatch_time_t poptime = Dispatch_time (Dispatch_time_now, (int64_t) (Delayinseconds * nsec_per_sec)); Dispatch_after (Poptime, Dispatch_get_main_queue (), ^ (void) { NSLog (@ "%@", _ TestButton.nextResponder.nextResponder.nextResponder.nextResponder); });
2013-09-21 03:56:22.043 Response Chain [690:60b] <UIApplication:0x15659c00>
Add one more:
2013-09-21 03:56:51.186 Response Chain [696:60b] <RSAppDelegate:0x16663520>
so does our appdelegate still have a parent node?
2013-09-21 03:57:22.588 Response Chain [706:60b] (NULL)
No, note that an event that is distributed from a leaf node can only be distributed to our appdelegate!
This tree structure is especially important in our project, for a chestnut, if we want to rewrite Uitouchevent's 4 methods in a view, and not affect his parent view to respond to these events, pay attention to the way you rewrite them. For example, we rewrite Touchbegan in Viewcontroller as follows:
-(void) Touchesbegan: (Nsset *) touches withevent: (uievent *)event{ NSLog (@ "Viewcontroller received touch event");}
The same paragraph is also written in Appdelegate:
-(void) Touchesbegan: (Nsset *) touches withevent: (uievent *)event{ NSLog (@ "Appdelegate received touch event");}
So who is being triggered?
2013-09-21 04:02:49.405 Response Chain [743:60b] Viewcontroller receiving touch Events
This is a good understanding, I have just said, Viewcontroller is obviously the appdelegate child node, he has the priority of event distribution. What if we want to trigger all two places? Here's a super-bit:
-(void) Touchesbegan: (Nsset *) touches withevent: (uievent *)event{ [Super touchesbegan:touches Withevent: event]; NSLog (@ "Viewcontroller received touch event");}
Output:
2013-09-21 04:07:26.206 Response Chain [749:60b] appdelegate receiving touch Events
2013-09-21 04:07:26.208 Response Chain [749:60b] Viewcontroller receiving touch Events
pay attention to the time stamp, appdelegate Although the priority level is inferior to viewcontroller, but he responds to the time above Viewcontroller 0.002 seconds earlier, I tried several times here, all is the difference 0.002 seconds.
So let's analyze how the responder chain works here:
The user finger touches the uiview, because we did not rewrite UIView's uitouchevent, so he and super executed the same, will continue to distribute the event to Uiviewcontroller;
Uiviewcontroller's Touchbegan was rewritten by us, and if we weren't super, then we'd write the response code here. The incident will not continue to be distributed here. It is conceivable that uiviewcontroller ancestor node: Uiwindow,uiapplication,appdelegate is not authorized to be distributed this event.
If we super Touchbegan, then this touch event is
Viewcontroller distributed to UIWindow,
UIWindow then distributed to UIApplication,
The uiapplication is distributed to Appdelegate,
So we captured this event in the Touchbegan method of Viewcontroller and Appdelegate.
You should have a good understanding of this responder tree here, right?
Let's talk about the first responder, and the event distribution on UIButton.
Responder's not a mystery. ———— iOS user responder chain complete anatomy