The most detailed iOS events in the history of the transmission and response mechanisms – Principles

Preface:

According to the time sequence of events, the life cycle is this:
events and transfer (how events from the parent control is transferred to the child controls and to find the bottom of the most appropriate view, looking for the most appropriate implementation of view, intercept events) -> to find the most appropriate handling of the incident response (view the touches method is overridden, events)

One of the key and difficult points are:
1 how to find the most appropriate view
2 to find the most appropriate view the underlying implementation (hitTest:withEvent: the underlying implementation)

(I) events in iOS

IOS events can be divided into 3 types:

  • Touch events
  • An event
  • Remote control event
    here we only discuss touch events in iOS.

1.1. responder object (UIResponder)

Learn to touch events first to understand a more important concept – responder object (UIResponder).

Not all objects can handle events in iOS, and only the objects inherited from UIResponder can accept and handle events". The following are inherited from UIResponder, so they can receive and handle events.
  • UIApplication
  • UIViewController
  • UIView

So why does a class that inherits from UIResponder receive and handle events?

  • Because UIResponder provides the following 4 object methods to handle touch events.
UIResponder provides the following methods to handle the event touch events - (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); - (void) motionBegan: accelerometer (UIEventSubtype) motion withEvent: (UIEvent * event); - (void) motionEnded: (UIEventSubtype) motion withEvent: (UIEvent * event); - (void) motionCancelled: (UIEventSubtype) motion withEvent: (UIEvent * event) remote control; event - (void) remoteControlReceivedWithEvent: (UIEvent * event);

(two) handling of events

UIView as an example to illustrate the handling of touch events.

/ / UIView is a subclass of UIResponder, can cover the following 4 different ways of handling touch events / one or more fingers to touch view, the system will automatically call the view - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent * event) / / one or more fingers on the view mobile. The system will automatically call the view (with the finger movement, will continue to call this method) - (void) touchesMoved: (NSSet * touches) withEvent: (UIEvent * event) / / one or more fingers left below view, the system will automatically call the method view (void) - touchesEnded: (NSSet *) touches withEvent: (UIEvent * event) / / touch before the end of a system event (e.g. incoming calls) will interrupt the process of touch, the system will automatically call view (void) - touchesCancelled: (NSSet Touches withEvent: (UIEvent) * * / / event) suggest that touches stored in the UITouch object

It should be noted that the above four methods are automatically called by the system, so you can override the method to handle some of the events.

  • If the two fingers at the touch of a view, then the view will only call a touchesBegan:withEvent: method, the touches parameters with 2 UITouch objects
  • If the two fingers touch the same view separately from the front, then view will call the 2 touchesBegan:withEvent: method, and the touches parameter of each call contains only one UITouch object
  • Override the above four methods, if it is handling the UIView touch event. Custom UIView subclasses must be inherited from UIView. Because Apple does not open source, did not provide the UIView.M file to us. We can only deal with the UIView’s touch event by inheriting the parent class from the subclass, overriding the subclass method (Note: I’m talking about the UIView touch event rather than the UIViewController touch event).
  • If you are dealing with the UIViewController touch event, then rewrite the four methods in the controller’s.M file!

Custom.H file **/ / * * UIView

#import < UIKit/UIKit.h> @interface WSView: UIView @end

The custom UIView.M file * / / * *

#import "WSView.h" @implementation WSView / / to the touch will call this method once - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@ "touch what I do! ");} / / finger movement will call this method / / this method is invoked very frequently (void) touchesMoved: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@" Oh, don't pull people! ");} / / finger off the screen will call this method once - (void) touchesEnded: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@" hand can continue to play! ");} @end

The.M file * / / * * controller

#import "ViewController.h" #import "WSView.h" @interface ViewController (@end@implementation) ViewController - (void) viewDidLoad [super viewDidLoad] {WSView *touchView; / / create custom view = [[WSView alloc] initWithFrame:CGRectMake (100, 100, 100, 100]); / / touchView.backgroundColor = [UIColor redColor] background color; / / add to the parent control [self.view addSubview:touchView] @end;}

Note: some people think that if I deal with view controller events do not need a custom subclass of UIView inherited from UIView, because you can override the touchBegan:withEvent: method in the viewController.m file but we discussed here is the UIView touch event, instead of processing UIViewController touch events. If you override the touchBegan:withEvent: method in the viewController.m file, the equivalent of treatment is viewController touch event, because viewController is inherited from UIResponder, so it will give people an illusion.
so, or that sentence, want to deal with the UIView touch event, you must customize the UIView subclass inherited from UIView.

2.1.UIView drag and drop

So, how to achieve UIView drag and drop it is to let UIView move along with the finger movement.
– override the touchsMoved:withEvent: method
at this time need to use the parameter touches, the following are the properties and methods of UITouch:

NS_CLASS_AVAILABLE_IOS (2_0) @interface UITouch: NSObject @property (nonatomic, readonly) NSTimeInterval timestamp @property (nonatomic, readonly); UITouchPhase phase; @property (nonatomic, readonly) NSUInteger tapCount down within a; / / touch certain point within a certain amount of time majorRadius and majorRadiusTolerance are in / / points / / The majorRadius will be accurate the / majorRadiusTolerance @property (nonatomic, readonly) CGFloat majorRadius NS_AVAILABLE_IOS (8_0); @property (nonatomic, readonly) CGFloat majorRadiusTolerance NS_AVAILABLE_IOS (8_0); @property (nullable, nonatomic, readonly, strong) UIWindow *window; @property (nullable, nonatomic, readonly, strong) UIView *view; @property (nullable, nonatomic, readonly, copy) NSArray < UIGestureRecognizer *> *gestureRecognizers NS_AVAILABLE_IOS; (3_2) - (CGPoint) locationInView: (nullable * UIView) view; (CGPoint) - previousLocationInView: (nullable * UIView) view of the; / / Force touch, where represents the force of an 1 Average touch @property (nonatomic, readonly) CGFloat force NS_AVAILABLE_IOS (9_0); / / Maximum possible force with this input mechanism @property (nonatomic, readonly) CGFloat maximumPossibleForce NS_AVAILABLE_IOS (9_0);

2.1.1.UITouch object

  • When the user touches the screen with a finger, it creates a UITouch object associated with the finger
  • A finger corresponds to a UITouch object
  • If the two fingers at the touch of a view, then the view will only call a touchesBegan:withEvent: method, the touches parameters with 2 UITouch objects
  • If the two fingers touch the same view separately from the front, then view will call the 2 touchesBegan:withEvent: method, and the touches parameter of each call contains only one UITouch object

Role of 2.1.1.1.UITouch

  • Keep information about the fingers, such as the location, time, and stage of the touch
  • When the fingers move, the system will update the same UITouch object, so that it can always keep the touch of the finger position
  • When the finger leaves the screen, the system will destroy the corresponding UITouch object
    tip: iPhone development, to avoid the use of double click event!

2.1.1.2.UITouch attributes

Touch generated at the window when the @property (nonatomic, readonly, retain) UIWindow *window; touch generated at the view of @property (nonatomic, readonly, retain) UIView *view; a short period of time according to the number of the screen, click, double-click can be judged or more click @property according to tapCount (nonatomic, readonly) NSUInteger tapCount record; touch events or changes the time unit is the second @property (nonatomic, readonly) NSTimeInterval timestamp; the current state of @property touch events (nonatomic, readonly) UITouchPhase phase;

2.1.1.3.UITouch method

(CGPoint) locationInView: (UIView * view); / / return value that touch on view position / location is here to return to view coordinates (view in the upper left corner of the origin (0, 0)) / / view parameters when calling the incoming nil, returns the touch point in the position of UIWindow (CGPoint) previousLocationInView: (UIView * view); / / this method records the position of the touch point of a

Code implementation:

- (void) touchesMoved: (NSSet * touches) withEvent: (UIEvent * event{) / want to move as the control finger movement and monitor finger movement to get the UITouch object UITouch / *touch = [touches anyObject]; / / get the current position of CGPoint curP = [touch locationInView:self]; / / get a point on the position of CGPoint preP = [touch previousLocationInView:self] gets the offset; / / X axis, every time is relatively a CGFloat offsetX = curP.x - preP.x; / / get the Y axis offset CGFloat offsetY = curP.y - preP.y; / / modify the control of the deformation or frame center, you can control the position of the control is also a relative deformation / deformation (translation) / / CGAffineTransformMakeTranslation: will be put before the deformation to the Qing Dynasty Empty, start setting deformation parameters / / make: / / deformation position relative to the original CGAffineTransform t: t the relative deformation on the basis of the go / / if the relative deformation which again deformation deformation, introduced its deformation of self.transform = CGAffineTransformTranslate (self.transform, offsetX, offsetY);}

(three) generation and delivery of events in iOS

Generation of 3.1. events

  • When a touch event occurs, the system adds the event to an event queue managed by UIApplication Because the queue is a first in first out, the first event to deal with the first to meet the common sense, so the event is added to the queue.
  • UIApplication takes out the most recent events from the event queue and distributes the event for processing, usually sending an event to the application’s main window (keyWindow).
  • The main window will find the most appropriate view in the view hierarchy to handle touch events, which is the first step in the event handling process. When
    finds the appropriate view control, it invokes the touches method of the view control for specific event handling. 3.2. event delivery
  • The transfer of a touch event is passed from parent to child
  • That is, UIApplication-> window-> look for the most appropriate view

Note: if the parent control does not accept touch events, the child controls are not likely to receive touch events

How do you find the most appropriate controls to handle events?
  • 1 first judge whether the main window (keyWindow) can accept the touch event
  • 2 to determine whether the touch point on their own
  • 3 sub control array from the front to traverse the sub control, repeat the previous two steps (called from the front to traverse the sub control, is the first to find the child control array in the last element, and then the implementation of the 1, 2 steps)
  • 4.view, for example, called fitView, then the event will be handed over to the fitView, and then traverse the fitView child controls, until there is no more appropriate view.
  • 5 if there is no qualified child controls, then consider themselves the most appropriate to deal with this event, that is, they are the most appropriate view.

UIView can not receive touch events in three cases:

  • No interaction: userInteractionEnabled = NO
  • Hidden: if the parent control is hidden, then the child control will be hidden, hidden controls can not accept events
  • Transparency: if you set the transparency of a control < 0.01, will directly affect the transparency of child controls. Alpha:0.0~0.01 is transparent.

Note: the default UIImageView cannot accept touch events because it does not allow interaction, i.e. userInteractionEnabled = NO, so if you want UIImageView to interact, you need userInteractionEnabled = YES.

Sum up

1 click on a UIView or generate a touch event A, which will be added to the A event queue managed by UIApplication (i.e., the first event to be received is UIApplication).
2.UIApplication will remove the most recent events from the event column (which is assumed to be a touch event A) and pass the event A to the application’s main window (keyWindow). The
3 window finds the most appropriate view in the view hierarchy to handle touch events. (at this point, the first step has been completed)
[image upload… (
) if you want a view to be unable to receive an event (or, if the event is passed to a view where it is broken), then it can be done in three ways as mentioned in the previous paragraph (). For example, set its userInteractionEnabled = NO; then the event is passed by the view parent control.
, for example, do not want to let the blue view receive events, you can set the blue view userInteractionEnabled = NO; then the resulting view click on the yellow or blue view event, orange view will become the most suitable view. Events are handled by the orange veiw.
so, regardless of whether the view can handle the event, as long as the click of the view will produce events, the key to see who the event is handled! That is, if the view cannot handle an event, click on the view, or a touch event will occur, but the event will not be processed by the clicked view!
note: if you set the transparency or hidden of the parent control, it will directly affect the transparency and hidden. If the parent control has a transparency of 0 or hidden = YES, then the child controls are invisible!

3.3. (heavy and difficult) how to find the most suitable view

How do you find the most appropriate controls to handle events?
1 first determine the main window (keyWindow) whether they can accept the touch event
2 touch points are in themselves
3 from the forward traversal control, repeat the previous two steps (the last element in the array to find the first 4
) if no child controls to meet the conditions, so that their most appropriate treatment

Details: 1 the main window to receive the application to pass the event, the first judge whether he can take over the touch event. If you can, then touch point in the window myself
2 if the touch points in the window body in the judgment, then the window will forward their child controls from the ergodic (ergodic own child controls just to find out the most appropriate view
3) to traverse each control, and two repeat steps (transfer events to the child controls, 1 judge child controls can accept 2 points in the event, not in the child control on
4) so looping through the child controls, until you find the most appropriate view, if there is no more appropriate child controls, so they become the most suitable view.
find the most appropriate view, it will call the view touches method to deal with specific events. Therefore, only to find the most appropriate view, the event is passed to the most appropriate view, will call the touches method for the next event handling. The most appropriate view is not found, and the touches method is not called for event processing.
note: the reason why you are going to traverse the child control from the back to find the most appropriate view is to do some loop optimization. Because, in contrast, after the addition of view in the above, reduce the number of cycles.

3.3.1. looking for the most appropriate view bottom profiling

Two important approaches:
hitTest:withEvent: method
pointInside method

3.3.1.1.hitTest:withEvent: Methods

When to call?

  • As long as the event is passed to a control, the control calls his own hitTest:withEvent: method

Effect

  • Find and return the most appropriate view (the most appropriate view to respond to the event)

Note: regardless of whether the control can handle events, and whether the touch point is not on the control, the event will be passed to the control, and then call the hitTest:withEvent: method

Intercept event handling

  • Just because hitTest:withEvent: the method can return the most appropriate view, so you can override the specified view as the most appropriate view by overriding the hitTest:withEvent method.
  • No matter where you click, the most appropriate view is hitTest:withEvent: the view returned in the method.
  • By rewriting the hitTest:withEvent: you can intercept the event of the transfer process, who want to deal with events who will handle the event.

Who will pass the event, who will call the hitTest:withEvent: method.
note: if nil returns hitTest:withEvent:, then call the method itself and its child controls are not the most appropriate view is in his body did not find a suitable view. Then the most appropriate view is the parent control.
so the event delivery order is this:
touch event -> UIApplication [UIWindow hitTest:withEvent:]-> -&gt event queue; return to the appropriate view-> [child control hitTest:withEvent:]-> return to the most appropriate view

After the event is passed to the window or control, call the hitTest:withEvent: method to find a more appropriate view. So, first pass the event, and then according to the event to find a more suitable view.
whether the child controls are not the most appropriate view, the default system must first sends the event to the child controls after their child controls to call the hitTest:withEvent: method validation after that there is no more appropriate view. Even if the parent control is the most appropriate view, the child control hitTest:withEvent: method will be called, or how to know if there is no more appropriate! That is, if the final parent control is the most appropriate view, then the hitTest:withEvent: method of the child control of the parent control is also called.
tip: who wants to be the most suitable view rewrite their own parent controls hitTest:withEvent: method returns the specified child control, or rewrite their own hitTest:withEvent: method return self. However, it is recommended that the child control be returned as the most appropriate hitTest:withEvent: in the parent control’s view!

The reason lies in the method of hitTest:withEvent: in their return to their own sometimes there will be a problem, because there is such a situation, when traversing child controls, if the touch point is not in the child control A himself but in the child control B, but also asked to return to the child control A as the most suitable view, the return to their the method may lead to yet to traverse the A themselves, there may have been some real traversal in the view, which is B. This led to the return of the view is not the real point of their own. Therefore, it is recommended to return the child control in the parent control hitTest:withEvent: as the most appropriate view!
example: whiteView has redView and greenView two sub controls. RedView first add, add after greenView. If there are requirements both click make redView as the most suitable view (the event to redView to deal with return) then only in the hitTest:withEvent: method in whiteView self.subViews[0] return in hitTest:withEvent:; in this case the redView method of self is not good!

Here is whiteView redView / / zeroth sub control #import "redView.h" @implementation - redView (UIView *) hitTest: (CGPoint) point withEvent: (UIEvent *) event{return self;} - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@ red-touch); / /}@end or #import "whiteView.h @implementation whiteView (UIView) hitTest: (CGPoint) point withEvent: (UIEvent *) event{return self.subviews[0];} - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@ white-touch);} @end

Special case:
who can not handle the event, the window can not handle.

  • Rewrite window hitTest:withEvent: method return nil

Only window handling events.

  • View controller hitTest:withEvent: method return nil or window hitTest:withEvent: method return self

The meaning of return nil:
hitTest:withEvent: return nil means that the current hitTest:withEvent: the method of view is not suitable for view, sub control is not suitable for view. If sibling control does not have the appropriate view, then the most appropriate view is the parent control.

To find the most appropriate hitTest:withEvent view underlying analysis: the underlying method of bottom
/ * * hitTest:withEvent: method **/ practice

#import "WSWindow.h" @implementation WSWindow / / call what time: as long as the event passed to a control, so this control will call this method / / my role: to find and return the most appropriate view / / UIApplication -> [UIWindow; hitTest: withEvent:] view tells the system to find the most suitable current finger touch point / / point: / / point: method the coordinate system is the point - (UIView) hitTest: (CGPoint) point withEvent: (UIEvent *) event{/ / 1 judgment can receive the event window if (self.userInteractionEnabled = = NO self.hidden = = YES || || self.alpha < return nil = 0.01); / / the 2 Judgment in the window in the window / not if ([self pointInside:point withEvent:event] return nil = = NO); / / the 3 from the back after the traversal of child controls An array of int count = (int) self.subviews.count; for (int i = count - 1; I = 0; > i--) {/ / UIView = self.subviews[i] *childView for child controls; / / coordinate system conversion, the conversion point on the window for child controls on their own on the control point / point into the sub the control points on the CGPoint childP convertPoint:point toView:childView] UIView = [self; *fitView = [childView hitTest:childP withEvent:event]; if (fitView) {/ / if you can find the most appropriate view return fitView;}} / / 4 did not find a suitable view, there is no more appropriate than their view return self;} / / function: incoming judgment over the point in the caller's coordinate method is point: / / caller sit The subject of the //- (BOOL) pointInside: (CGPoint) point withEvent: (UIEvent * event) //{/ / return NO; //} - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent *) event{NSLog (@ "%s", __func__);} @end

The hit:withEvent: method calls the pointInside:withEvent: method at the bottom level to determine the point in the coordinate system of the method caller.

3.3.1.2.pointInside:withEvent: method

PointInside:withEvent: method to determine the point in not in the current view (method caller coordinates) if it returns YES, coordinate method on representative points in the caller; return NO representative points in the caller method of the coordinate system, then the caller also cannot handle events.


(four) incident response

The whole process of 4.1. touch event handling

1> a touch event is generated after the user clicks on the screen, after passing a series of process, will find the most suitable view control to handle the 2&gt event; to find the most suitable view control, the touches method will call the control to the specific event processing touchesBegan… TouchesMoved… TouchedEnded… 3> the default behavior of these touches methods is to pass the event up the responder chain (that is, the touch method does not handle the event by default, only passing the event), and then the event is passed on to the previous responder

Schematic diagram of 4.2. responder chain

The responder chain: iOS program in either a UIWindow or behind the front button, they are placed in context, a control can put another control above or below, then the user clicks on a particular control is above or below the trigger control control, which has the relationship between a chain called the responder chain”. It can be said that the responder chain is a chain that is connected by a plurality of responder objects. In iOS, the relationship between the responder chain can be shown in the following figure:

The most detailed iOS events in the history of the transmission and response mechanisms - Principles

response object: to handle the event, which is inherited from the
object UIResponder: can clearly see each response relationship, and can let an event handle multiple objects.

How to determine the last responder

  • 1> if the current view is the view of the controller, the controller is the last responder
  • 2> if the current view is not the view of the controller, the parent control is the last responder

Event transfer process of responder chain:

  • 1> if the current view is view controller, then the controller is a response, the event is transmitted to the controller; if the current view is not a view controller, then the parent view is the current view on the response of a person, event is passed to its parent view
  • 2> at the top level of the view hierarchy, if it is not able to handle the event or message that is received, the event or message is passed to the window object for processing
  • 3> if the window object is not processed, the event or message is passed to the UIApplication object
  • 4> if UIApplication cannot handle the event or message, discard it

The whole process of event processing summary:
1 touch screen touch events, touch events will be added to the event queue managed by UIApplication (i.e., first to receive the event is UIApplication).
2.UIApplication will remove the most recent events from the event queue and pass the event to the application’s main window (keyWindow). The
3 main window finds the most appropriate view in the view hierarchy to handle touch events. (at this point, the first step has been completed)
4 is the most appropriate view will call the touches method to their handling of events
5.touches default approach is to put the events along the responder chain up.
touches default practice:

#import "WSView.h" @implementation WSView / / click control, it will call the touchBegin, if not override this method, you can't handle a response / touch event is likely to be the parent control - (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent * event{) / / default passes the event to the response of a person in response, a person is the parent control, to control [super touchesBegan:touches father withEvent:event]; / / note not to call the parent control's touches method, but call the parent class touches / super is the parent class superview is the parent control} @end

Transfer and response events:
1, when an event occurs, the event will be from the parent control to control, that is to say by UIApplication -> UIWindow -> UIView -> initial view, the above is the transfer event, is the process of finding the most suitable view.

2, followed by the event response. First look at the initial view can handle this issue, if not the incident will be passed to the superior view (inital view superView); if the view is still unable to handle will continue to transfer; has been passed to the view controller view controller, first determine whether the root view view view controller can handle this event; if not it then determines the view controller can handle this event, if you still can not continue to transfer; (for the second view controller itself in another view controller, then continue to the root view, the parent view controller if not view root to the parent view controller) has been to; window, if window is unable to handle this event will continue to turn to application, if application is not. Dispose of this event and discard it

3, in the event of the response, if a control implements the touches method, the event will be accepted by the control, if the call to [supertouches… “; will response to events along the chain transfer, transfer to a response; then one would call the touches response… Method

How to do an event to handle multiple objects:
system because the default approach is to put the event up to the parent control, so you can rewrite your own touches and father touches control method to achieve the objective of a multiple event processing object.

- (void) touchesBegan: (NSSet * touches) withEvent: (UIEvent *) event{/ / 1 to handle events... NSLog (@ "do somthing..."); / / 2 and then call the system default, then the event gave a response to [super touchesBegan:touches withEvent:event];}

The difference between the transmission and response events:
transfer events from top to bottom (parent to child controls), incident response is from bottom to top (along the responder chain to transfer: the child controls to the parent control.

The VV / wood son (author Jane)
PS: as non specified, all articles are original works, the copyright of the author of all reprint, reproduced please contact the author authorized, and indicate the source, all rewards are all belong to me!

If you are a iOS developer, or interested in this article, please pay attention to me, the follow-up will update more related articles! Coming soon!