概念
響應者 : 對使用者互動動作事件進行響應的物件。
響應者鏈:成為處理事件的響應者的先後順序鏈。
正文
1、Hit-Test 機制
當使用者觸控(Touch)螢幕進行互動時,系統首先要找到響應者(Responder)。系統檢測到手指觸控(Touch)操作時,將Touch 以UIEvent的方式加入UIApplication事件佇列中。UIApplication從事件佇列中取出最新的觸控事件進行分發傳遞到UIWindow進行處理。UIWindow 會通過hitTest:withEvent:
方法尋找觸碰點所在的檢視,這個過程稱之為hit-test view。
hitTest 的順序如下
1 |
UIApplication -> UIWindow -> Root View -> ··· -> subview |
在頂級檢視(Root View)上呼叫pointInside:withEvent:方法判斷觸控點是否在當前檢視內;
如果返回NO,那麼hitTest:withEvent:
返回nil;
如果返回YES,那麼它會向當前檢視的所有子檢視傳送hitTest:withEvent:
訊息,所有子檢視的遍歷順序是從最頂層檢視一直到到最底層檢視,即從subviews陣列的末尾向前遍歷,直到有子檢視返回非空物件或者全部子檢視遍歷完畢。
如果有subview的hitTest:withEvent:返回非空物件則A返回此物件,處理結束(注意這個過程,子檢視也是根據pointInside:withEvent:的返回值來確定是返回空還是當前子檢視物件的。並且這個過程中如果子檢視的hidden=YES、userInteractionEnabled=NO或者alpha小於0.1都會並忽略);
如果所有subview遍歷結束仍然沒有返回非空物件,則hitTest:withEvent:
返回self;
系統就是這樣通過hit test找到觸碰到的檢視(Initial View)進行響應。
2、響應者鏈 (Responder Chain)
有些時候,Touch後系統通過hit test 機制找到了觸碰到的Initial View,但是Initial view並沒有或者無法正常處理此次Touch。這個時候,系統便會通過響應者鏈尋找下一個響應者,以對此次Touc 進行響應。
響應者鏈順序如下:
1 |
Initial View -> View Controller(如果存在) -> superview -> · ·· -> rootView -> UIWindow -> UIApplication |
如果一個View有一個檢視控制器(View Controller),它的下一個響應者是這個檢視控制器,緊接著才是它的父檢視(Super View),如果一直到Root View都沒有處理這個事件,事件會傳遞到UIWindow(iOS中有一個單例Window),此時Window如果也沒有處理事件,便進入UIApplication,UIApplication是一個響應者鏈的終點,它的下一個響應者指向nil,以結束整個迴圈。
3實際開發中常見的相關問題
在實際開發中,經常會遇到檢視沒有響應的情況,特別是新手會經常搞不清楚狀況。
一下是檢視沒有響應的幾個情況:
1.userInteractionEnabled=NO;
2.hidden=YES;
3.alpha=0~0.01;
4.沒有實現touchesBegan:withEvent:
方法,直接執行touchesMove:withEvent:
等方法;
5.目標檢視點選區域不在父檢視的Frame上 (superView背景色為clear Color的時候經常會忽略這個問題)。
在某些情景下,我們在點選子檢視的時候仍然需要呼叫父檢視的touchesBegan:withEvent:
等方法,例如我們在父檢視上新增了一個覆蓋範圍了父檢視大部分面積的TableView或ScrollerView 或其他View,而我們要通過父檢視的touchesBegan:withEvent:
方法來收鍵盤。
這個時候我們可以通過UIView的類別,重寫touch相關方法,程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
-(void)setEnableNextResponder:(BOOL)enableNextResponder { objc_setAssociatedObject(self, &enableNextResponderKey, enableNextResponderKey, OBJC_ASSOCIATION_ASSIGN); } -(BOOL)enableNextResponder { return objc_getAssociatedObject(self, &enableNextResponderKey); } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ if (self.enableNextResponder) { [[self nextResponder] touchesBegan:touches withEvent:event]; [super touchesBegan:touches withEvent:event]; } } -(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.enableNextResponder) { [[self nextResponder] touchesEnded:touches withEvent:event]; [super touchesEnded:touches withEvent:event]; } } -(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.enableNextResponder) { [[self nextResponder] touchesMoved:touches withEvent:event]; [super touchesMoved:touches withEvent:event]; } } -(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { if (self.enableNextResponder) { [[self nextResponder] touchesCancelled:touches withEvent:event]; [super touchesCancelled:touches withEvent:event]; } } |
首發連結:http://www.jianshu.com/p/31e9b9e16411
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式