觸摸事件處理的詳細過程:
當用戶點擊屏幕後產生一個觸摸事件,經過經過一系列的傳遞過程後,會找到最合適的視圖控件來處理這個事件,找到最合適的視圖之後,就會調用空間的touches那三個方法,這些方法的默認做法是把事件順着響應者鏈條向上傳遞,將事件傳遞給上一個響應者進行處理。
傳遞過程
UIApplication
接收到事件,將事件傳遞給Window
。Window
遍歷subViews
的hitTest:withEvent:
方法,找到點擊區域內合適的視圖來處理事件。UIView
的子視圖也會遍歷其subViews
的hitTest:withEvent:
方法,以此類推。- 直到找到點擊區域內,且處於最上方的視圖,將視圖逐步返回給
UIApplication
。 - 在查找第一響應者的過程中,已經形成了一個響應者鏈。
- 應用程序會先調用第一響應者處理事件。
- 如果第一響應者不能處理事件,則調用其
nextResponder
方法,一直找響應者鏈中能處理該事件的對象。 - 最後到
UIApplication
後仍然沒有能處理該事件的對象,則該事件被廢棄。
怎麼尋找最合適的view?用到以下的兩個方法。
// 此方法返回的View是本次點擊事件需要的最佳View
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
// 判斷一個點是否落在範圍內
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
事件傳遞給窗口或控件的後,就調用hitTest:withEvent:方法尋找更合適的view,如果子控件是合適的view,則在子控件再調用hitTest:withEvent:查看子控件是不是合適的view,一直遍歷,直到找到最合適的view,或者廢棄事件。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.alpha <= 0.01 || self.userInteractionEnabled == NO || self.hidden) {
return nil;
}
BOOL inside = [self pointInside:point withEvent:event];
if (inside) {
NSArray *subViews = self.subviews;
// 對子視圖從上向下找
for (NSInteger i = subViews.count - 1; i >= 0; i--) {
UIView *subView = subViews[i];
CGPoint insidePoint = [self convertPoint:point toView:subView];
UIView *hitView = [subView hitTest:insidePoint withEvent:event];
if (hitView) {
return hitView;
}
}
return self;
}
return nil;
}
總結:
1、首先判斷UIControl還是UIResponder;
2、如果是UIResponder -(從上往下) 響應鏈找到最合適的視圖 ;如果是 UIControl- UIApplication (如果類似view1有btn和tap 則響應btn,如果view1有btn,btn上有tap,則響應tap)
3、有手勢先手勢,同級的話先看有沒有UIControl。
tips:
1、手勢不參與響應者鏈傳遞事件,但是也通過hitTest
的方式查找響應的視圖,手勢和響應者鏈一樣都需要通過hitTest
方法來確定響應者鏈的。在UIApplication
向響應者鏈派發消息時,只要響應者鏈中存在能夠處理事件的手勢,則手勢響應事件,如果手勢不在響應者鏈中則不能處理事件,處理最合適的view的響應。
2、如果同一視圖有UIGesture和UIButton,會響應UIButton的響應事件;
3、如果同一視圖添加多個UIGesture,只會響應最上面的。