大多數事件的分發都是依賴響應鏈的。
響應鏈
是由一系列連接在一塊兒的響應者
(UIResponse子類:UIApplication
、UIViewController
、UIView
)組成的。通常狀況下,一條響應鏈開始於第一響應者,結束於application對象。若是一個響應者不能處理事件,則會將事件沿着響應鏈傳到下一響應者。ios
@property(nonatomic, readonly, nullable) UIResponder *nextResponder;
複製代碼
事件被蘋果分爲3種大類型: 觸摸事件,加速計事件以及遠程遙控事件git
當一個事件發生後,事件會從父控件傳給子控件,也就是說由github
硬件
->系統
->UIApplication
->UIWindow
->SuperView
->SubView
bash以上就是事件的傳遞,也就是尋找
第一響應者
的過程。 符合第一響應者
的條件包括:app
- touch事件的位置在響應者區域內
pointInside:withEvent: == YES
- 響應者
self.hidden != NO
- 響應者
self.alpha > 0.01
- 響應者
self.userInteractionEnabled = YES
- 遍歷 subview 時,是從上往下順序遍歷的,即 view.subviews 的 lastObject 到 firstObject 的順序,找到合適的響應者view,即中止遍歷.
第一響應者
對於接收到的事件有3種操做:ide
- 不攔截,默認操做。事件會自動沿着默認的響應鏈往下傳遞
- 攔截,再也不往下分發事件。重寫
touchesBegan:withEvent:
進行事件處理,不調用父類的touchesBegan:withEvent:
- 攔截,繼續往下分發事件。重寫
touchesBegan:withEvent:
進行事件處理,同時調用父類的touchesBegan:withEvent:
將事件往下傳遞
下圖展現了Hit-Testing的邏輯ui
UIView
是做爲UIViewController
根視圖存在的,則其nextResponder
爲UIViewController
對象;UIWindow
上的,則其nextResponder
爲UIWindow對象。// 若觸摸發生在UITextField上,則事件的傳遞順序是:
UITextField ——> UIView ——> UIView ——> UIViewController ——> UIWindow ——> UIApplication ——> UIApplicationDelegation
複製代碼
superview
superview
也嘗試來處理事件,若是他處理不了,繼續傳遞他的父視圖 UIViewcontroller.view
UIViewController.view
嘗試來處理該事件,若是處理不了,將把該事件傳遞給 UIViewController
UIViewController
嘗試處理該事件,若是處理不了,將把該事件傳遞給主窗口 Window
Window
嘗試來處理該事件,若是處理不了,將傳遞給應用單例 Application
Application
也處理不了,則該事件將會被丟棄事件的傳遞是從上到下(父控件到子控件),事件的響應是從下到上(順着響應者鏈條向上傳遞:子控件到父控件。atom
若是當前這個view是控制器的view,那麼控制器就是上一個響應者 若是當前這個view不是控制器的view,那麼父控件就是上一個響應者spa
若是view
的控制器存在,就傳遞給控制器;若是控制器不存在,則將其傳遞給它的父視圖 在視圖層次結構的最頂級視圖,若是也不能處理收到的事件或消息,則其將事件或消息傳遞給 window
對象進行處理 若是 window 對象也不處理,則其將事件或消息傳遞給 UIApplication
對象 若是 UIApplication
也不能處理該事件或消息,則將其丟棄(銷燬)code
由於系統默認作法是把事件上拋給父控件,因此能夠經過重寫本身的touches方法和父控件的touches方法來達到一個事件多個對象處理的目的。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.本身先處理事件...
NSLog(@"do somthing...");
// 2.再調用系統的默認作法,再把事件交給上一個響應者處理
[super touchesBegan:touches withEvent:event];
}
複製代碼
重寫子view的point:inside`` →
擴大Button的點擊區域(上下左右各增長20)重寫父view的point:insde`` →
子view超出了父view的bounds響應事件userInteractionEnabled = NO
,結果是包括父控件在內的全部子控件都不能處理觸摸事件(雖然設置透明度和hidden=YES
也能夠,可是那樣就看不見了注意:若是父控件的透明度設置爲0或者hidden=YES,那麼子控件也是不可見的。)isUserInteractionEnabled=false
,想讓它繼續繼續處理觸摸事件,能夠在它的父控件的hitTest方法中直接返回它。point:inside:
方法point:inside:
方法point:inside:
方法返回false,則對應的hitTest返回nilpoint:inside:
方法返回true,則調用對應的hitTest方法重複上面的操做返回子控件的最合適子控件UIGestureRecongnizer
、UIContorl
均可以處理觸摸事件UIGestureRecognizer
:使用addGestureRecognizer
方法處理事件UIControl
:使用addTarget
方法處理事件UIResponder
:使用touches
等一系列方法處理事件
UIButton繼承自UIControl
,UIControl繼承自UIView
,若是給UIButton
添加了手勢,並實現了本身的處理事件的>>方法,當點擊UIButton
的時候發現touches
方法走了,手勢方法(addGestureRecognizer
)也走了,本身的方法(addTarget
)沒有走。由此能夠得出一個結論:
UIGestureRecognizer的優先級
> >UIContol的優先級
,當一個UIButton即實現了本身的方法(addTarget
),又添加了手勢方法(addGestureRecognizer
)的話,本身的方法(addTarget
)會被屏蔽掉,不論是否添加了手勢,touches
方法都會處理。