
官方文檔,摘取一部分說明:
“當(dāng)用戶 與 iPhone的觸摸屏產(chǎn)生互動時,硬件就會探測到物理接觸并且通知操作系統(tǒng)。接著操作系統(tǒng)就會創(chuàng)建相應(yīng)的事件并且將其傳遞給 當(dāng)前正在運行的應(yīng)用程序的事件隊列。然后 這項事件會被事件循環(huán)傳遞給優(yōu)先響應(yīng)者物件。優(yōu)先響應(yīng)者物件 是 事件 被觸發(fā)時 和 用戶 交互的物件,比如 按鈕物件、視圖物件。如果 我們 編寫了 代碼 讓 優(yōu)先響應(yīng)者 處理 這種類型的事件,那么 它 就會處理 這種類型的事件。處理完 某項事件后,響應(yīng)者 有 兩個選項:1、將 其 丟棄;2、將 其 傳遞給 響應(yīng)鏈條中的下一個響應(yīng)者。下一個響應(yīng)者的地址 存儲 在當(dāng)前響應(yīng)者物件所包含的變量nextResponder當(dāng)中。如果 優(yōu)先響應(yīng)者 無法處理 一項事件,那么 這項事件 就傳遞給 下一個響應(yīng)者,直到 這項事件 到達 能處理它的響應(yīng)者 或者 到達 響應(yīng)鏈條的末端,也就是 UIApplication類型的物件。UIApplication類型的物件 收到 一項事件后,也是要么處理要么丟棄?!?/p>
分發(fā)機制分兩步:
1: 事件傳遞鏈: 從上往下
2: 事件響應(yīng)鏈: 從下往上
hitTest 的順序如下
UIApplication -> UIWindow -> Root View -> ··· -> subview
事件響應(yīng)順序:可以通過nextResponder來獲取下一個響應(yīng)responder,優(yōu)先響應(yīng)view綁定的vc
Initial View -> View Controller(如果存在) -> superview -> · ·· -> rootView -> UIWindow -> UIApplication

主要用到的方法
1、尋找hitTestView(即最適合響應(yīng)這個事件的view),用到兩個方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
2、響應(yīng)時主要用到UIResponder里定義的方法,注意:只有找到真正的hitTestView,才會走事件響應(yīng)鏈,調(diào)用下面的響應(yīng)方法
如
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
重點:復(fù)現(xiàn)hitTest方法源碼,可以看出是如何找到hittestview的:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
return self;
}
return nil;
}
hitTest:withEvent:方法可能會被系統(tǒng)調(diào)用多次(蘋果開發(fā)者解釋為:系統(tǒng)可能會調(diào)整touch point,所以會多次調(diào)用hit test---https://lists.apple.com/archives/cocoa-dev/2014/Feb/msg00118.html
)
響應(yīng)鏈
//找離得最近的vc
- (UIViewController *)vc{
id next = [self nextResponder];
while(![next isKindOfClass:[ViewController class]]){
return next;
}
return nil;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 這里可以做子view自己想做的事,做完后,事件繼續(xù)上傳,就可以讓其父類,甚至父viewcontroller獲取到這個事件了
[[selfnextResponder]touchesBegan:toucheswithEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[[selfnextResponder]touchesEnded:toucheswithEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[[selfnextResponder] touchesCancelled:toucheswithEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[[selfnextResponder] touchesMoved:toucheswithEvent:event];
}