The problem

  • iOSViewWhat happened to the incidentpassandThe response?
  • whyThe parent ViewWhen the event response is turned off,The child ViewCan’t respond to events? Underlying principles?
  • How to expandButtonClick range?
  • How to makeThe parent ViewThe child ViewSimultaneously responding to the same event? By default, only responses are madeThe child ViewEvent callback.
  • whyThe child ViewClosed the event but itsThe parent ViewTo open the event case, clickThe child ViewWhen,The parent ViewCan you respond to events properly?
  • Why is it that if a child View is A UIView, if you don’t add a gesture, when you click on a child View, it’s going to get a response from its parent View, but if a child View is a UIControl, if it doesn’t add a gesture, it’s not going to get a response from its parent View
  • .

Analysis of the

IOS events fall into three categories

  • Touch Events
  • Motion Events, such as gravity and a shake.
  • Remote Events(e.g. using buttons on the headset to control your phone)

The whole process of Touch Events can be divided into two stages: transmission and response.

  • Transfer: finding the best fit for us when we touch the screenView.
  • Response: When we find the best fitViewAfter, this time just found the most suitableViewBut not necessarilyViewYou can respond to this event, so you need to continue to find one that canView.

Delivery process

Whenever a finger touches the screen, the operating system will pass the event to the current App. After UIApplication receives the finger event, it will call ‘UIWindow hitTest:withEvent:’ to see if the currently clicked point is in the window. If so, continue calling the hitTest:withEvent: method of its subView until the last view is found. Once the call is complete and the hit-test view is determined, the most appropriate view can be determined.

  • Quote a few pictures to illustrate

  • The content represented by the picture can be described here in the following words

Recursion starts by sending a hitTest:withEvent: message to the root UIWindow of the interface, and returning from that message is a UIView, which is the hitTest view in front of the current finger position. So when I send a hitTest:withEvent: message to UIWindow, what I’m doing inside hitTest:withEvent: is I’m trying to determine if the current click position is inside the window, If yes, the window subview is iterated and then the subview is sent hitTest:withEvent: message. If the current point is not on the view, the view’s subview will not be traversed. When the event traverses view B.1, point is found in View B.1, and view B.1 has no subview, then it is the hittest view that we are looking for, and it will return all the way to the root node, and view A after View B will not be traversed.

  • The following is- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)eventAn internal implementation of a method
 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if(self.hidden || ! Self. UserInteractionEnabled | | self. Alpha < 0.01 | |! [self pointInside:point withEvent:event] || ! [self _isAnimatedUserInteractionEnabled]) {return nil;
    } else {
        for (UIView *subview in[self.subviews reverseObjectEnumerator]) { UIView *hitView = [subview hitTest:[subview convertPoint:point fromView:self]  withEvent:event];if (hitView) {
                returnhitView; }}returnself; }}Copy the code

The code above comes from here

The response process

  • My understanding of the response process is as follows:

When we know the most appropriate View, the event will be from top down [child View -> parent View, controller View -> controller] to find the appropriate View to respond to the event, to respond to the relevant event. If the current View has a gesture, then it responds to the corresponding event without further search. If there is no gesture event, then it implements the following method:

	- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
	- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
	- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
	- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
Copy the code

If there is an implementation then it responds from the View, if there is no implementation then it is passed to its next responder (child View -> Parent View, controller View -> controller), here we can do a simple verification, by default UIView does not respond to events, UIControl will respond even if it doesn’t add a gesture, so you can use the Runtime to look at the list of UIView and UIControl methods, or you can look at the UIKit source code and you can see that UIView doesn’t implement the touchesBegan method, And UIControl implements the method above, so it verifies that UIView is not responding, and UIControl is responding. Once the View that best fits the response is found, it is terminated, and the binding event of the response is executed, if not, the event is discarded.

My validation

  • It can handle events when gestures are added first.
  • So let’s create A View A and add A View B to A, and if we add A gesture to A and no gesture to B,
  • When we click on B, it responds to A’s event, which is perfectly normal, so how does it determine whether B can handle it?
  • Now we add a gesture to B, so the same action will trigger B’s gesture, now we add a method to B,
	@implementation BMSonView
	- (NSArray<UIGestureRecognizer *> *)gestureRecognizers {
	    NSLog(@"% @", self);
	    return@ []; }Copy the code

Gestures return @[]. Clicking ON B will only trigger events in A, so you can check whether gestureRecognizers can handle events. As mentioned above, there is also a method to determine whether the following method is implemented. By default, UIView does not implement the following method, so it does not respond to events when no gesture is added.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
Copy the code

If we implement the above method manually, clicking on B will not respond to A’s method, even if no gesture is added to B. UIControl source can be cleared to see.

So personal understanding:

  • The event is passed in relation to the hit method above, and the [window –> view] implementation determines which view is the most appropriate one to respond to

  • In response, and up and down to find the first view to deal with the event, [view –> window], in the process of looking for just will determine whether to increase the gesture and whether to achieve the above touch method.

  • As for the UIControl Button’s special event response, I think it is in its M file to implement the above 4 methods, in these 4 methods to do the relevant processing, here you can know some content from the UIControl code.

  • So if you want to implement your own UIControl Button, you first have to figure out how to handle the above four methods.

  • Figure as follows

Problem solving

  • How do View events in iOS actually pass and respond?

As described above.

  • Why can’t a child View respond to an event when the parent View closes the event response?

Because the event is passed to the parent view first, when the parent view can not respond to the event, directly skipped through the child view, so as long as the parent class closed the event, the child view has no chance to respond to the event.

  • How to enlarge Button click range?

To expand the click range, you want BTN to respond to the event without clicking BTN, so you can do an appropriate operation in the hitTest method. When XXX condition is met, forcibly return BTN to achieve the best click range effect. Relevant implementation can Google. There are some more elegant and concise ways.

  • How do I make a parent View and a child View respond to the same event?

The parent View and the child View respond to the same event at the same time. By default, when you click on the child View, if the ziView can handle the event, then the other parent View will not respond, but we know that in the hitTest method when the parent View passes to the child View, The child view and the parent view respond to events at the same time.

  • Why does the child View turn off the event, but the parent View turns on the event, and the parent View responds to the event when the child View is clicked?

The child view closes the event, the event is the parent view to the child view, in the parent view, the parent view can respond, then will continue to access its child view can respond, if the child view can not respond, then it will directly return to the parent view, So it’s inevitable that the child View closes the event and the parent View executes the event normally.

  • Why is it that if a child View is A UIView, if you don’t add a gesture, when you click on a child View, it’s going to get a response from its parent View, but if a child View is a UIControl, if it doesn’t add a gesture, it’s not going to get a response from its parent View

This problem can be solved by looking for a responsive View above. UIControl implements the above four methods, but UIView does not.

  • There’s a lot more to discover here, like the event response of a ScrollView.

The resources

  • SMNH. Me/hit – testing…
  • Zhoon. Making. IO/ios / 2015/04…
  • Southpeak. Making. IO/blog / 2015/0…
  • Developer.apple.com/library/ios…
  • Developer.apple.com/library/ios…
  • Developer.apple.com/library/ios…
  • Developer.apple.com/library/ios…

The statement

  • Brief introduction to the transmission and response process of iOS events, the original text is from here, the original text is kept updated and maintained, other platforms do not guarantee real-time update