iOS development-Event delivery response chain

Source: Internet
Author: User

Preface

When we are using tools like this, click Sweep to open the QR Code scan view. When we clicked on the screen, the iphone OS got the user "click" Behavior, the operating system wraps the information containing these click events into Uitouch and uievent forms, and then finds the currently running program, looking for the object that can respond to the event. Until there is no responder response. This search process, called the event's response chain, as shown, is not used by the responder in a chained way to find

Event Response Chain Responder

In iOS, the object that is capable of responding to an event is a UIResponder subclass object. UIResponderprovides four user clicks of the callback method, corresponding to the user click Start, move, click End and Cancel the click, only when the program forced to quit or call, cancel the Click event will be called.

Uiresponder Click events


When customizing UIView as a base class control, we can override these methods to make a click callback. In the callback, we can see that the method receives two parameters, a collection of objects, and UITouch an UIEvent object. These two parameters represent the Click Object and the Event object respectively.

    • Event Object
      iOS uses UIEvent an event object that represents the user interaction, and in the UIEvent.h file we can see that there is a UIEventType type of property that represents the current response event type. Multiple touch, shake, and remote operation (3DTouch event type added after iOS). The object is unique during a user click event process UIEvent

    • Click Object
      UITouchRepresents a single click that has an enumeration type attribute in its class file that UITouchPhase represents the state of the current click. These states include Click Start, move, stop, end, and cancel five states. Each time a click occurs, the Click object is placed in a collection of incoming UIResponder callback methods, we get the location of the user click through the objects in the collection. - (CGPoint)locationInView:(nullable UIView *)viewIt - (CGPoint)previousLocationInView:(nullable UIView *)view obtains the coordinate point of the last click position by obtaining the current click Coordinate point.

In order to confirm that UIView is indeed a click-through response to the UIResponder click event, I created the UIView category and overridden the + (void)load method to swap the implementation of the Click event using the Method_swizzling way

12345678910111213141516171819202122232425262728293031 + (void)load    Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:));    Method custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesBegan:withEvent:));    method_exchangeImplementations(origin, custom);    origin = class_getInstanceMethod([UIView class], @selector(touchesMoved:withEvent:));    custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesMoved:withEvent:));    method_exchangeImplementations(origin, custom);    origin = class_getInstanceMethod([UIView class], @selector(touchesEnded:withEvent:));    custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesEnded:withEvent:));    method_exchangeImplementations(origin, custom);} - (void)lxd_touchesBegan: (NSSet *)touches withEvent: (UIEvent *)event{    NSLog(@"%@ --- begin", self.class);    [self lxd_touchesBegan: touches withEvent: event];}- (void)lxd_touchesMoved: (NSSet *)touches withEvent: (UIEvent *)event{    NSLog(@"%@ --- move", self.class);    [self lxd_touchesMoved: touches withEvent: event];}- (void)lxd_touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event{    NSLog(@"%@ --- end", self.class);    [self lxd_touchesEnded: touches withEvent: event];}

In the new project, I created the subclasses of Aview, Bview, CView, and Dview four UIView respectively, and then click anywhere:

Project Structure diagram


When I click on the Green view, the console outputs the following log (the date part has been removed):

12 CView --- beginCView --- end

This shows that when we click on UIView, the callback is handled by touches related click events.

In addition to several click events for the touches callback, gesture UIGestureRecognizer objects can also be attached to the view to implement other rich gesture events. After you add a click gesture to a view, the original touchesended method is invalid. At first I always thought that after the view added gesture, the original touches series methods were all invalid. But in the test demo, after the view added gesture, the Touchesbegan method has a callback, but moved and ended do not have a callback. Therefore, in the system's touches event processing, after Touchesbegan, there should be a scheduling follow-up event (Nexthandler) processing method, the personal guessing event scheduling processing is as follows:

Event Dispatch response chain delivery

The above has described the handling of a control when it receives a click event, so how does the system find the view that handles the Click event from the point where the user clicked?
As we have said above, the system responds to click events by constantly looking for the next responder, and all the interactive controls are UIResponder direct or indirect subclasses, can we find the key attributes in the header file of this class?

There is just one way: - (nullable UIResponder *)nextResponder by means of a method name we are not difficult to find that this is the next responder to get the current view, then we rewrite the Touchesbegan method to get the next responder, until there is no next responder location. The relevant code is as follows:

1234567891011 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{    UIResponder * next = [self nextResponder];    NSMutableString * prefix = @"".mutableCopy;    while(next != nil) {        NSLog(@"%@%@", prefix, [next class]);        [prefix appendString: @"--"];        next = [next nextResponder];    }    }

All subordinate event responders for the console output are as follows:

123456 AView--UIView----ViewController------UIWindow--------UIApplication----------AppDelegate

Although the results are very hierarchical, the order of the output is reversed from the point of view of the system's stepwise search for the responder. Why does this problem occur? We can see that there is a Viewcontroller class in the output that describes UIViewController UIResponder the subclass as well. But we can find that the controller is a view manager, even if it is a member of the response chain, but according to logic, controllers should not be one of the system lookup objects, through the Nextresponder method to find this idea is not correct.

Later, found in UIView the header file there are so two methods, respectively, to return UIView and BOOL type the method:

12 - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;   // recursively calls -pointInside:withEvent:. point is in the receiver‘s coordinate system- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   // default returns YES if point is in bounds

Depending on the method name, one is whether the event occurs in this view based on the click Coordinates, and the other method is to return the object that responds to the Click event. With these two methods, we can guess that the system gets the view of the next response by continually iterating through these methods of the child view on the current view when it receives the Click event. Therefore, the implementation of these two methods continues to be modified by the Method_swizzling method, and the test output is as follows:

12345678910111213141516171819202122 UIStatusBarWindow can answer 1UIStatusBar can answer 0UIStatusBarForegroundView can answer 0UIStatusBarServiceItemView can answer 0UIStatusBarDataNetworkItemView can answer 0UIStatusBarBatteryItemView can answer 0UIStatusBarTimeItemView can answer 0hit view: UIStatusBarhit view: UIStatusBarWindowUIWindow can answer 1UIView can answer 1hit view: _UILayoutGuidehit view: _UILayoutGuideAView can answer 1DView can answer 0hit view: DViewBView can answer 0hit view: BViewhit view: AViewhit view: UIViewhit view: UIWindow......  //下面是touches方法的输出

The top uistatusbar of the beginning of the type you may not have seen, but it does not prevent us from guessing this is a few of the status bar related views, you can find Apple's Document Center (Xcode shortcut key shift+command+0 Open). It is not difficult to see from the output that the system calls to pointInSide: WithEvent: determine whether the current view and the child views of those views can receive the Click event, and then in order to hitTest: withEvent: get all the view objects that handle the event, after getting all the processed event objects, the touches callback method of the object is called.

With the method call of the output, we can see that the response is in the order of the lookup: the UIStatusBar related View-- UIWindow UIView AView DView BView ---------------the system will traverse all the sub-views during the event chain Pass To respond to a click event), as an example of this demo, we can draw the following diagram of the event response chain lookup:

Responder Lookup Process

Then after the lookup responder process is complete, the system will convert the clicks in this event into UITouch objects, and then pass these objects and UIEvent types of event objects to the Touchesbegan method, you

Not only that, but from the nextresponder output above, all responders return a view that responds to clicks in the lookup. Therefore, we can infer that the UIApplication object maintains its own responder stack, and when it pointInSide: withEvent: returns Yes, the responder is in the stack.

Responder Stack


The responder at the top of the stack acts as the object of highest priority, assuming that the Aview does not handle the event, then the stack is handed over to UIView, so that it continues until the event is processed or arrives at Appdelegate and remains unresponsive until the event is discarded. With this mechanism we can also see that the controller is an exception in the responder stack, and even if no pointInSide: withEvent: method returns to respond, the controller can still be UIView the next responder to the stack.

Response Chain Application

Now that we know how the system gets the flow of the response view, we can implement irregular shape clicks by overriding the method of finding the event handler. The most common irregular view is a circular view, in which I set the view's width height to 200, then the override method event is as follows:

12345678 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{    const CGFloat halfWidth = 100;    CGFloat xOffset = point.x - 100;    CGFloat yOffset = point.y - 100;    CGFloat radius = sqrt(xOffset * xOffset + yOffset * yOffset);    returnradius <= halfWidth;}

The final is as follows:

Irregular shape click
      • Demo Address

iOS development-Event delivery response chain

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.