An iOS device will generate a variety of events (UIEvent instances), such as touch screen, remote control, etc. When these events occur, a responder (UIResponder instance) is required to respond to these events. This requires an event response mechanism.

The event type

Looking at the definition of UIEventType, we know that there are four event types.

typedef NS_ENUM(NSInteger, UIEventType) {
    UIEventTypeTouches,
    UIEventTypeMotion,
    UIEventTypeRemoteControl,
    UIEventTypePresses NS_ENUM_AVAILABLE_IOS(9_0),
};
Copy the code

UIEventTypeTouches are events created by touching a phone’s screen. UIEventTypeMotion what we can access is probably the shake of the phone, which is the shaking event. UIEventTypeRemoteControl and UIEventTypePresses are generally not encountered by mobile App developers. The name UIEventTypePresses is somewhat confusing, but it actually refers to the pressing of physical buttons, such as the TV remote control.

With the exception of UIEventTypeTouches, we have a hard time dealing with them.

Event responder and response chain

The ones that respond to events are UIResponder and its subclasses. The common subclasses of UIResponder are: UIView, UIViewController, UIApplication, and UIApplicationDelegate

The following figure is from Apple’s official documentation. It can be seen that the response chain of events is basically consistent with the hierarchical relationship of the view. It is worth noting that the end of the response chain is… – > UIWindow – > UIApplication – > UIApplicationDelegate. A UIResponder’s nextResponder points to its nextResponder. You can override the nextResponder method to change the nextResponder. In fact, some classes have overridden nextResponder, such as a UIView whose nextResponder points to the View Controller if it’s the root view of UIController, or to its parent view if it’s not. See the section Using Responders and the Responder Chain to Handle Events, which is called Altering the Responder Chain.

If no responder is found along the response chain that can respond to the event, the event is ignored.

We see that the UIResponder corresponding to each type of event has a corresponding incident response method, such as for touch events have touchesBegan: withEvent:, touchesMoved: withEvent: touchesCancelled: WithEvent:, touchesEnded: withEvent: and for motion events have motionBegan: withEvent:, motionEnded: withEvent: motionCancelled: WithEvent:. And there are methods for other events, which you can look at in the UIResponder class.

If a UIResponder subclass overrides the above event-response method, the event is considered to be responded to by an instance of that class. Events are no longer passed down the response chain. A UIControl (UIButton is a subclass of UIControl), whether it has a target added or not, the event passes to it and ends there. UIControl should implement the above methods for responding to events.

For example, a UIView subclass overrides the touchesBegan: withEvent:, touchesMoved: withEvent: touchesCancelled:, touchesEnded: withEvent: WithEvent: if a touch event is passed to an instance of this subclass, these methods will be called and the passing down will stop.

Event passing mechanism

The event response chain described above has a starting point, which is called a first responder. Each type of event has a rule for finding or assigning a first responder, so we’re only interested in touch events.

When the phone is touch screen, the system will be the packaged into touch events (touch event) and passed to the UIApplication, UIApplication is passed to the UIWindow (is also a subclass of UIView), called UIWindow hitTest: withEvent:, Get a UIView that responds to touch events

UIView’s hitTest:withEvent: method traverses the view hierarchy to find the deepest subview containing a particular touch, called the first responder for the touch event. If the first responder cannot respond to the event, the event is passed along the event response chain until it is responded to or ignored.

The image above shows how hitTest:withEvent: is implemented. Some of the points we need to pay attention to, first of all judgment is hidden, userInteractionEnable and alpha these attributes, and then use the pointInside: withEvent: to determine whether a click event occurs within the UIView. You then iterate through all the subviews in reverse order and call their hitTest:withEvent: method.

Several applications of the principles of event passing mechanism

1. Non-rectangular clickable areas. For example, one circular area of a button can be clicked and the other areas cannot be clicked. Just rewrite pointInside:withEvent: to return YES on points in the circular region and NO on points not in the circular region.

2. The button is out of the superview. By default, buttons beyond the superview are not clickable. We can override the pointInside:withEvent: method of the superview to return YES for parts of the button that are outside the superview.

Reference:

1. Using Responders and the Responder Chain to Handle Events