touch handling时先hitTest:withEvent:找出hitView:
1  | - (UIView *)hitTest:withEvent: {  | 
Responder Chain
然后UIWindow就sendEvent:给这个view。hitView遵循responderChain:
- 若hitView实现了
touchesBegan:,不管有没实现其他3个方法:调hitView的touchesBegan:方法,再调hitView的其他3个方法(如果实现了的话) - 若hitView没实现
touchesBegan:,但实现了其他3个方法:沿chain找到第一个响应touchesBegan:的ancestorView,其他3个方法在沿着chain从[hitView -> ancestorView]的各个view上依次调用。 
responderChain基本就是沿着superview转发touch。如果这个superview是viewController.view,那实际上是将touch交给viewController处理。例如,parentView -> parent’s viewController -> window -> application -> applicationDelegate。
但我们不能手动将touch传给nextResponder(下面的写法不太work):
1  | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  | 
Gesture Recognizer
gestureRecognizer不依赖responderChain。从[hitView -> window]链上的所有gestureRecognizers都可以处理touch。touchesBegan:和touchesMoved:会同时发给hitView和[hitView -> window]链上的所有gestureRecognizers。在touchesEnded阶段,如果沿链有gestureRecognizer识别成功就给hitView发touchesCancelled:(默认gestureRecognizer.cancelsTouchesInView == YES),如果识别失败就给hitView发touchesEnded:withEvent:。
gestureRecognizer默认不能与其他gestureRecognizers同时识别成功,可以重载delegate方法:gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
gestureRecognizer的默认属性:
- cancelsTouchesInView (default YES),识别成功时向touch.view发
touchesCancelled: - delaysTouchesBegan (default NO),
touchesBegan:会同时发往touch.view,若设为YES则得等自己识别失败才把touchesBegan:发往touch.view - delaysTouchesEnded (default YES),例如,这样就不会把双击识别成两次单击
 
UIScrollView
scrollView不管有没实现touchesBegan:等4个方法,都会自己处理touch不会向responderChain转发。这很可能是因为scrollView默认实现了touchesBegan:方法。
webView即使实现了touchesBegan:等4个方法也不会调用到,touch都被隐藏嵌套的_UIWebViewScrollView(: UIWebScrollView : UIScrollView)吞掉了,交给干活的子view,如UIWebBrowserView(: UIWebDocumentView : UIWebTiledView : UIView)处理。由于webView内部scrollView会吞掉touch,甚至在touchMoved几次后把touch.view变成nil。要对将传给webView的touch做其他处理,可重载UIWindow的sendEvent:,先截获感兴趣的touch做处理,再[super sendEvent:event];走正常流程将touch传给webView。
默认scrollView.delaysContentTouches == YES,即scrollView有个内装定时器的gestureRecognizer,设置有gestureRecognizer.delaysTouchesBegan = YES,要等这小定时器超时gestureRecognizer识别失败才给view转发touchesBegan:消息。当快速滚动scrollView时,panGestureRecognizer立即识别成功,识别成功的gestureRecognizer默认cancelsTouchesInView。由于touchesBegan:消息还没发出,不再发送touchesCancelled:,直接把touchesBegan:消息丢掉。
Others
若target小于44pt不好点中,应重载target的pointInside:withEvent:来扩展可点击区域CGRectInset(self.bounds, -expansion, -expansion)
参考:
- Event Handling Guide for iOS
 - Advanced ScrollViews and Touch Handling Techniques, WWDC2014#235
 - Enhancing User Experience with Scroll Views, WWDC2012#223
 - iPhone Multi-touch Event Handling
 - swiping and pinching with UIWebView