iOS touch
1. User touch
2. event is created
3. hit testing by coordinates - find first responder - UIView and successors (UIWindow)
3.1 hit testing - recursive find the most deep view(the smallest)
3.1.1 point inside - check coordinates
4. Send Touch Event to the First Responder
Class diagram
3 Hit Testing
Find a First Responder
First Responder in this case is the deepest(the smallest) UIView, point() method(hitTest() uses point() internally) of which returned true. It always go through UIApplication -> UIWindow -> First Responder
func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
func point(inside point: CGPoint, with event: UIEvent?) -> Bool
Internally hitTest() looks like
func hitTest() -> View? {
if (isUserInteractionEnabled == false || isHidden == true || alpha == 0 || point() == false) { return nil }
for subview in subviews {
if subview.hitTest() != nil {
return subview
}
}
return nil
}
4 Send Touch Event to the First Responder
//UIApplication.shared.sendEvent()
//UIApplication, UIWindow
func sendEvent(_ event: UIEvent)
//UIResponder
func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?)
Let's take a look at example

Responder Chain
It a kind of chain of responsibility pattern. It consists of UIResponser who can handle UIEvent. In this case it starts from first responder who overrides touch.... super.touch... calls next link in responder chain
Responder chain is also used by addTarget or sendAction approaches like event bus
//UIApplication.shared.sendAction()
func sendAction(_ action: Selector, to target: Any?, from sender: Any?, for event: UIEvent?) -> Bool
Take a look at example
class AppDelegate: UIResponder, UIApplicationDelegate {
@objc
func foo() {
//this method is called using Responder Chain
print("foo") //foo
}
}
class ViewController: UIViewController {
func send() {
UIApplication.shared.sendAction(#selector(AppDelegate.foo), to: nil, from: view1, for: nil)
}
}
*isExclusiveTouch is taken into account when handling multitouch
[Android onTouch]