傳遞鏈
- 傳遞鏈: Application -> window -> root view -> ... -> first view
- UIResponse:響應(yīng)對象的基類钙态,定義了事件處理的接口
- 常見的子類: UIView,UIViewController氮双,UIApplication以及所有繼承自UIView的UIKit類都直接或間接的繼承自UIResponder
- 藍(lán)色箭頭為事件的傳遞過程瞎饲,紅色箭頭為事件響應(yīng)的過程
- 當(dāng)發(fā)生點(diǎn)擊事件后,系統(tǒng)會(huì)將事件加入到UIApplication管理的一個(gè)任務(wù)隊(duì)列中
- UIApplication將處于任務(wù)隊(duì)列最前端的事件向下分發(fā)給UIWindow
- UIWindow將事件向下分發(fā)給View
- UIView首先看自己是否能夠處理事件,觸摸點(diǎn)擊是否在自己身上卦洽,如果能,那么繼續(xù)尋找子視圖(遞歸天添加順序)
- 遍歷子控件斜棚,重復(fù)以上兩步
- 如果沒有找到阀蒂,那么window自己就是事件處理
- 如果自己不能處理,那么不做任何處理
- 事件傳遞的兩個(gè)核心方法
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool
-
hitTest
方法用來尋找哪一個(gè)視圖來響應(yīng)這個(gè)事件 -
point(inside
方法用來判斷某一個(gè)點(diǎn)擊的位置是否在視圖范圍內(nèi)弟蚀,如果在返回true - View不接受事件處理的情況有:
- alpha < 0.01
- userInteractionEnable = false
- hidden = true
- 超出父控件響應(yīng)區(qū)域
事件傳遞流程
- 點(diǎn)擊屏幕產(chǎn)生觸摸事件蚤霞,系統(tǒng)將這個(gè)事件加入到一個(gè)由UIApplication管理的事件隊(duì)列中,UIApplication會(huì)從消息隊(duì)列里取事件分發(fā)下去义钉,首先傳給UIWindow
- 在UIWindow中就會(huì)調(diào)用hitTest:withEvent:方法去返回一個(gè)最終響應(yīng)的視圖
- 在hitTest:withEvent:方法中就會(huì)去調(diào)用pointInside: withEvent:去判斷當(dāng)前點(diǎn)擊的point是否在UIWindow范圍內(nèi)昧绣,如果是的話,就會(huì)去遍歷它的子視圖來查找最終響應(yīng)的子視圖
- 遍歷的方式是使用倒序的方式來遍歷子視圖捶闸,也就是說最后添加的子視圖會(huì)最先遍歷夜畴,在每一個(gè)視圖中都回去調(diào)用它的hitTest:withEvent:方法,可以理解為是一個(gè)遞歸調(diào)用
- 最終會(huì)返回一個(gè)響應(yīng)視圖删壮,如果返回視圖有值斩启,那么這個(gè)視圖就作為最終響應(yīng)視圖,結(jié)束整個(gè)事件傳遞醉锅;如果沒有值兔簇,那么就會(huì)將UIWindow作為響應(yīng)者
- 傳遞鏈?zhǔn)怯脕慝@取到一個(gè)響應(yīng)控件(第一響應(yīng)者)
響應(yīng)鏈
- 自底向上傳遞
- 傳遞鏈獲取獲取到了第一響應(yīng)者,接下來就開始響應(yīng)事件
- 響應(yīng)鏈: first view -> super view -> ... -> view controller -> window -> application -> appdelegate
- 找到最適合的響應(yīng)視圖后事件會(huì)從此視圖開始沿著響應(yīng)鏈nextResponder傳遞,直到找到處理事件的視圖,如果沒有處理的事件會(huì)被丟棄垄琐。
- 如果視圖有父視圖則nextResponder指向父視圖
- 如果是根視圖則指向控制器边酒,最終指向AppDelegate, 他們都是通過重寫nextResponder來實(shí)現(xiàn)
響應(yīng)傳遞示例
- 如果label不處理事件,UIKit 將事件發(fā)送到label的父視圖view
- 如果view不處理事件狸窘,則將事件發(fā)送給super view
- 如果superview不處理事件墩朦,繼續(xù)層層查找,直到根視圖翻擒,根視圖的nextResponder指向ViewController
- 如果viewController不處理事件氓涣,則將事件傳遞給window
- 如果window不處理事件,則將事件傳遞給UIApplication
- UIApplication不處理事件陋气,則將事件傳遞給Appdelegate
- 如果都沒有找到合適的對象處理劳吠,則事件被拋棄
應(yīng)用
- 利用響應(yīng)鏈獲取view的控制器
extension UIView {
var viewController: UIViewController? {
var next = next
var current: UIViewController?
while next != nil {
if next is UIViewController {
return next as? UIViewController
}
next = next?.next
}
return current
}
}
- 指定按鈕的點(diǎn)擊范圍
- 修復(fù)子視圖超出父視圖無法點(diǎn)擊
- 兩個(gè)覆蓋的button,分別響應(yīng)不同的事件