When the 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 perform a hit-test to find which object should receive the event. - The method
hitTest:withEvent
is used to find the view at the touch location. - The method
pointInside :withEvent:
is used to detect if the click is within the bounds of the view.hitTest:withEvent
will callpointInside: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 the below methods.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
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 setting a breakpoint at the code that processes this event. For example a button’s buttonPressed method.
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.
- Do nothing ( not to overload the above method ).
- Handle some of it, and leave the rest to other objects ( Call super object’s related method at the end of each overridden method ).
- Process the event alone ( Do not call super object’s related method when override method ).
- If the first responder doesn’t handle anything, or handle parts and then calls the super method, the event will be sent through a chain-style response path which is called a response chain. This event is forwarded along the following path.
- First responder.
- The parent view of the first responder.
- The parent view of the parent view, up to the associated view controller.
- The parent view controller of the view controller is up to the root view.
- The next responder to the root view is a window.
- The next responder to the window is an application.
- 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 an 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 are the priorities for event handling.
Touch Related Gesture Event ---> UIControl SubClass Event ---> Touch Event.
3.2 iOS Event Process Classification.
- If you want to change the event distribution process, you can implement the
hitTest:withEvent:
method for the related class. - If you want to expand the scope of the click area, you can override
pointInside:withEvent:
method. - 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];
- If you want the parent view or associated view controller to handle the event, you can use the below code.
[button addTarget:nil action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];