本文主要解析從咱們的手指觸摸蘋果設備到最終響應事件的整個處理機制。本質上講,整個過程能夠分爲兩個步驟:html
步驟1:找目標。在iOS視圖層次結構中找到觸摸事件的最終接受者;ios
步驟2:事件響應。基於iOS響應者鏈(Responder Chain)處理觸摸事件git
在找目標階段所使用到的兩大利器是UIView的 hitTest:withEvent: 以及 pointInside:withEvent: 方法。找目標的過程也稱爲hit-Testing。先來看一張圖(注: 圖來自MJ)比較直觀:github
下面解釋一下處理原理:編程
一、手指觸摸屏幕,這個動做被包裝成一個UIEvent對象發送給當前活躍的UIApplication (Active Application),Application將該Event對象插到任務隊列的末尾等待處理(先進先出,先來的先處理);數組
二、UIApplication單例將事件發送給APP的主Window(全部顯示的view都添加在Window上);app
三、主Window調用視圖層次結構上逐級使用hit-Testing確認最終的響應目標,這個目標也稱爲hitTesting view。ide
在沒有作任何重載操做的前提下,系統默認的hit-Testing的處理機制以下:spa
當前view調用自身的pointInside: withEvent:方法判斷觸摸點是否在本身範圍內:code
以上就是第一階段尋找響應view的機制。這裏咱們結合一個具體的例子再過一遍(圖片引自技術哥的博客):
當用戶點擊ViewD所在的區域時會進行如下hit-Testing:
這裏有幾點須要強調:
一、hitTest方法調用pointInside方法;
二、hit-Testing過程是從superView向subView逐級傳遞,也就是從層次樹的根節點向葉子節點傳遞;
三、遇到如下設置時,view的pointInside將返回NO,hitTest方法返回nil:
hit-Testing過程用代碼能夠描述以下:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { if (self.alpha <= 0.01 || !self.userInteractionEnabled || self.hidden) { return nil; } BOOL inside = [self pointInside:point withEvent:event]; UIView *hitView = nil; if (inside) { NSEnumerator *enumerator = [self.subviews reverseObjectEnumerator]; for (UIView *subview in enumerator) { hitView = [subview hitTest:point withEvent:event]; if (hitView) { break; } } if (!hitView) { hitView = self; } return hitView; } else { return nil; } }
上一部分咱們經過hit-Testing機制找到了hitTesting View,這個hitTesting View就是觸摸事件的響應者Responder。在iOS系統中,可以響應並處理事件的對象稱之爲Responder Object,而UIResponder是全部responder的最頂層基類。當hitTesting view作完本身該作的動做後,能夠根據須要將消息傳給下一級響應者。那下一級響應者會是什麼呢?這取決於iOS中的響應者鏈Responder Chain,以下圖所示:
更具體的:
瞭解響應者鏈有時候能夠幫我解決一些實際問題。我舉個例子,咱們知道,當提供給你一個ViewController你能夠很容易獲得它的view,一句代碼的事情:
viewWanted = someViewController.view;
但若是反過來呢?當給你一個view,讓你找到其所在的ViewController呢?這時候響應者鏈能夠幫上忙了,代碼以下:
@implementation UIView (FindController) -(UIViewController*)parentController{ UIResponder *responder = [self nextResponder]; while (responder) { if ([responder isKindOfClass:[UIViewController class]]) { return (UIViewController*)responder; } responder = [responder nextResponder]; } return nil; } @end
這篇文章解析了iOS響應觸摸事件的機制。或許你如今找不到這個知識的應用點,可是一旦你理解了,能夠幫助你實現一些特別的需求,好比點擊某個按鈕,響應的倒是另外一個按鈕;穿透某個view點擊到view下面的view... 更有甚者,你能夠用上面的知識解決不規則區域觸摸問題(看我以前的文章)、不添加任何view就能擴大控件的可觸摸區域等。天馬行空,任我翱翔!
=======================================================
原創文章,轉載請註明 編程小翁@博客園,郵件zilin_weng@163.com,歡迎各位與我在C/C++/Objective-C/機器視覺等領域展開交流!
歡迎跳轉個人GitHub主頁,關注個人開源代碼。也歡迎你Star/Fork/Watch個人項目。
=======================================================