關(guān)鍵點:hitTest:withEvent方法的底層實現(xiàn)
//point是該試圖的坐標系上的點
-(UIView *)hitTest:(CGpoint)point withEvent:(UIEvent *)event {
//判斷自己是否接受觸摸事件
if(self.userInteractionEnable = NO || self.hidden = YES || self.alpha <= 0.01) return nil;
//判斷觸摸點在不在自己的范圍內(nèi)
if(![self pointInside:point withEvent:event]) return nil;
int count = self.subviews.count;
//從后往前便利自己的控件菊匿,看是否有子空間更適合響應此事件
for(int i = count - 1;i >=0;i--){
UIView *childView = self.subviews[i];
CGPoint childPoint = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childPoint withEvent:event];
if(fitView) {
return fitView;
}
}
//沒有找到比自己更合適的view
return self;
}
- 總結(jié)幾個點
1、在hitTest:消息分發(fā)過程中计福,并不是所有包含了觸摸點的view都會經(jīng)歷事件傳遞跌捆,只有pointInside返回YES的view才屬于響應者鏈條。
2象颖、響應者是什么佩厚?繼承了UIResponder的對象稱為響應者對象,能夠處理touchesBegan等事件说订。很多響應者鏈接在一起的組合的一個鏈條稱為響應者鏈條抄瓦。
3、重寫hitTest和pointInside可以做到好多功能陶冷。
使用案例
1钙姊、例如不想讓某個視圖響應事件,只需要重寫pointInside:withEvent:方法埂伦,讓此方法返回NO就行了煞额。
2、隔層透傳
3、擴大點擊區(qū)域
2膊毁、響應者鏈條使用案例
- 使用響應者鏈條找到當前view所屬的控制器
- (UIViewController *)parentController {
UIResponder *responder = [self nextResponder];
while(responder){
if([responder isKindofClass:[UIViewController class]]) {
return (UIViewController *)responder;
}
responder = [responder nextResponder];
}
return nil;
}
3胀莹、nextResponder
鏈條:
subview->view->viewController->window->application->AppDelegate->nil
4、系統(tǒng)事件source0婚温?那它是如何喚醒mainThread的runloop的呢描焰?
- 1、用戶觸發(fā)事件栅螟, IOKit.framework 生成一個 IOHIDEvent 事件并由 SpringBoard 接收荆秦,SpringBoard會利用mach port,產(chǎn)生source1嵌巷,來喚醒目標APP的com.apple.uikit.eventfetch-thread的RunLoop。
- 2室抽、 Eventfetch thread會將main runloop 中__handleEventQueue所對應的source0設(shè)置為signalled == Yes狀態(tài)搪哪,同時喚醒main RunLoop。mainRunLoop則調(diào)用__handleEventQueue進行事件隊列處理坪圾。