iOS中加載的時候會先執行main函數ide
int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
根據main函數的參數加載UIApplication->AppDelegate->UIWindow->UIViewController->superView->subViews
關係爲:UIApplication.keyWindow.rootViewController.view.subView函數
那麼,系統是怎麼找到接收觸摸事件發生的視圖的?spa
只經過UIView及其子類查找,調用根視圖的hitTtest:withEvent,其的執行過程以下:3d
iOS使用hit-testing尋找觸摸的view。 Hit-Testing經過檢查觸摸點是否在關聯的view邊界內,若是在,則遞歸地(recursively)檢查該view的全部子view。在層級上處於lowest(我理解就是離用戶最近的view)且邊界範圍包含觸摸點的view成爲hit-test view。肯定hit-test view後,它傳遞觸摸事件給該view。code
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { // 1.判斷當前控件可否接收事件 if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil; // 2. 判斷點在不在當前控件 if ([self pointInside:point withEvent:event] == NO) return nil; // 3.從後往前遍歷本身的子控件 NSInteger count = self.subviews.count; for (NSInteger i = count - 1; i >= 0; i--) { UIView *childView = self.subviews[i]; // 把當前控件上的座標系轉換成子控件上的座標系 CGPoint childP = [self convertPoint:point toView:childView]; UIView *fitView = [childView hitTest:childP withEvent:event]; if (fitView) { // 尋找到最合適的view return fitView; } } // 循環結束,表示沒有比本身更合適的view return self; }
其中,-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)eventblog
這個函數的用處是判斷當前的點擊或者觸摸事件的點是否在當前的view中。繼承
它被hitTest:withEvent:調用,經過對每一個子視圖調用pointInside:withEvent:決定最終哪一個視圖來響應此事件。若是 PointInside:withEvent:返回YES,而後子視圖的繼承樹就會被遍歷(遍歷順序中最早響應的爲:與用戶最接近的那個視圖。 it starts from the top-level subview),即子視圖的子視圖繼續調用遞歸這個函數,直到找到能夠響應的子視圖(這個子視圖的hitTest:withEvent:會返回self,而不是nil);不然,視圖的繼承樹就會被忽略。遞歸