iOS事件鏈有兩條:事件的響應(yīng)鏈;Hit-Testing事件的傳遞鏈
- 響應(yīng)鏈:由離用戶最近的view向系統(tǒng)傳遞。initial view –> super view –> ….. –> view controller –> window –> Application –> AppDelegate
- 傳遞鏈:由系統(tǒng)向離用戶最近的view傳遞。UIKit –> active app's event queue –> window –> root view –> …… –> lowest view
在iOS中只有繼承UIResponder的對象才能夠接收并處理事件,UIResponder是所有響應(yīng)對象的基類,在UIResponder類中定義了處理上述各種事件的接口。我們熟悉的UIApplication、UIViewController、UIWindow和所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder,所以它們的實(shí)例都是可以構(gòu)成響應(yīng)者鏈的響應(yīng)者對象。
1. 傳遞鏈
- 事件傳遞的兩個(gè)核心方法
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
第一個(gè)方法返回的是一個(gè)UIView,是用來尋找最終哪一個(gè)視圖來響應(yīng)這個(gè)事件
第二個(gè)方法是用來判斷某一個(gè)點(diǎn)擊的位置是否在視圖范圍內(nèi),如果在就返回YES
- 其中UIView不接受事件處理的情況有
1. alpha <0.01
2. userInteractionEnabled = NO
3. hidden = YES
- 事件傳遞的流程
- 我們點(diǎn)擊屏幕產(chǎn)生觸摸事件,系統(tǒng)將這個(gè)事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中,UIApplication會從消息隊(duì)列里取事件分發(fā)下去,首先傳給UIWindow
- 在UIWindow中就會調(diào)用hitTest:withEvent:方法去返回一個(gè)最終響應(yīng)的視圖
在hitTest:withEvent:方法中就會去調(diào)用pointInside: withEvent:去判斷當(dāng)前點(diǎn)擊的point是否在UIWindow范圍內(nèi),如果是的話,就會去遍歷它的子視圖來查找最終響應(yīng)的子視圖- 遍歷的方式是使用倒序的方式來遍歷子視圖,也就是說最后添加的子視圖會最先遍歷,在每一個(gè)視圖中都回去調(diào)用它的hitTest:withEvent:方法,可以理解為是一個(gè)遞歸調(diào)用
- 最終會返回一個(gè)響應(yīng)視圖,如果返回視圖有值,那么這個(gè)視圖就作為最終響應(yīng)視圖,結(jié)束整個(gè)事件傳遞;如果沒有值,那么就會將UIWindow作為響應(yīng)者
2. 響應(yīng)鏈
響應(yīng)者鏈的事件傳遞過程
- 如果view的控制器存在,就傳遞給控制器處理;如果控制器不存在,則傳遞給它的父視圖
- 在視圖層次結(jié)構(gòu)的最頂層,如果也不能處理收到的事件,則將事件傳遞給UIWindow對象進(jìn)行處理
- 如果UIWindow對象也不處理,則將事件傳遞給UIApplication對象
- 如果UIApplication也不能處理該事件,則將該事件丟棄