iOS Event Delivery Introduces And Example

When iOS application starts, the UIApplicationMain function will create a single instance of a UIApplication. This single instance maintains a FIFO queue for event distribution. When the system detects a touch event, it will send the event to the current singleton application to distribute. The distribution is divided into three processes.

1. hitTest.

Once UIWindow receives an event, it will performs a hit-test to find which object should receive the event. hitTest:withEvent method is used to find the view at the touch location. pointInside :withEvent: used to detect if the click is within the bounds of the view. hitTest:withEvent will call pointInside:withEvent:.

hitTest is called recursively until the top leaf view that can handle touch events is found and is selected ( It’s usually the view of the region where the finger point at ). The view is also called the first responder.

This process can be understood by set breakpoints in below methods.

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event

hit test process

2. sendEvent.

Once the first responder is identified, the UIApplication singleton sends the associated touch event to the first responder. This process can be understood by set breakpoint at the code that process this event. For example a button’s buttonPressed method.

ios event dilevery send event

3. Process Event.

To handle a touch event you should override the following methods.

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; 
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;

The first responder has three options after receiving the event.

  1. Do nothing ( not to overload  above method ).
  2. Handle some of it, and leave the rest to other objects ( Call super object related method  at the end of each override method ).
  3. Process the event alone ( Do not call super object related method when override method ).

If the first responder doesn’t handle anything, or handle parts and then call the super method, The event will be sent through a chain style response path which is called response chain. This event is forwarded along the following path.

  1. First responder.
  2. The parent view of the first responder.
  3. The parent view of the parent view, up to the associated view controller.
  4. The parent view controller of the view controller up to the root view.
  5. The next responder to the root view is window.
  6. The next responder to the window is application.
  7. The last responder was the App delegate.

If it is forwarded to the App delegate, and the App delegate does not handle it, the event is discarded.

3.1 iOS Event Handling Priority.

When you handle iOS event, you should process the event in a subclass of UIControl, the event type can be a touch event or touch related gesture.

The gesture event will be processed first, if no gesture event is detected then the UIControl event such as ( button tap, button click ) will be triggered, if no such event has been captured, the TouchEvent will be processed.

Below is the priorities for event handling.

Touch Related Gesture Event —> UIControl SubClass Event —> Touch Event.

3.2 iOS Event Process Classification.

  1. If you want to change the event distribution process, you can implement the hitTest:withEvent: method for the related class.
  2. If you want to expand the scope of the click area, you can override pointInside:withEvent: method.
  3. To solve a problem such as putting away the keyboard, try sending an event directly like below.
    [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
  4. If you want the parent view or associated view controller handle the event, you can use below code.
    [button addTarget:nil action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];