需求
利用圖片道川,實(shí)現(xiàn)一個(gè)如圖的按鈕組。
遇到的問題
如下圖所示:
- 功能1立宜、2冒萄、3、4的按鈕可以實(shí)現(xiàn)點(diǎn)擊功能橙数。但是在紅色方框四角的位置尊流,也會(huì)響應(yīng)相應(yīng)的點(diǎn)擊事件。
- 紫色方框內(nèi)四角區(qū)域點(diǎn)擊時(shí)灯帮,響應(yīng)的方法是功能5崖技,而不是對(duì)應(yīng)的功能。
解決思路
期望的結(jié)果
- 尋找到合適的Button來處理點(diǎn)擊事件
需要弄明白的問題
- 事件在如何傳遞的钟哥?
- 怎么判斷誰來處理當(dāng)前事件迎献?
事件是如何傳遞的?
- 當(dāng)用戶觸摸實(shí)際屏幕時(shí)腻贰,會(huì)生成一個(gè)Touch Event吁恍,將此事件添加到UIApplication管理的事件隊(duì)列之中。
- UIApplication從事件隊(duì)列之中按順序取出事件分發(fā)到視圖去處理播演。
- 當(dāng)事件被發(fā)出以后冀瓦,會(huì)從keyWindow開始,依次向上傳遞写烤,包括Controller以及View翼闽,最后找到合適的視圖來響應(yīng)事件。
可以看出:當(dāng)一個(gè)事件發(fā)生后洲炊,事件會(huì)從父控件傳給子控件感局,也就是說由UIApplication -> UIWindow -> UIView -> initial view,以上就是事件的傳遞,也就是尋找最合適的view的過程选浑。
涉及到兩個(gè)方法:
func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
func point(inside point: CGPoint, with event: UIEvent?) -> Bool
當(dāng)UIApplication發(fā)送事件到keyWindow時(shí)蓝厌,會(huì)調(diào)用 hitTest來尋找最合適的視圖處理事件。判斷邏輯如下:
首先判斷自身是否能夠響應(yīng)觸摸事件(userInteractionEnabled==true古徒、hidden==true拓提、alpha<=0.01不能響應(yīng)觸摸事件),若能響應(yīng)則下一步,否則返回nil隧膘。
如果可以響應(yīng)觸摸事件代态,調(diào)用pointInside來判斷是否在顯示區(qū)域內(nèi)寺惫,如果不在其中,pointInside返回false蹦疑,同時(shí)hitTest返回nil西雀。
如果 pointInside返回true,表示在當(dāng)前的視圖之中歉摧,然后<font color="red">倒序</font>遍歷該視圖的子視圖,重復(fù)上述步驟艇肴,直到某一視圖可以響應(yīng),hitTest:返回該視圖叁温。
-
如果執(zhí)行完上述步驟以后再悼,沒有符合條件的視圖響應(yīng)事件,則返回視圖本身膝但,表示只有當(dāng)前視圖符合條件冲九,能夠處理該事件。
Q:為什么倒序遍歷跟束? A:因?yàn)樵趕ubViews數(shù)組中莺奸,最后添加的視圖,在視圖層級(jí)中處于最上方冀宴。
怎么判斷誰來處理當(dāng)前事件灭贷?
當(dāng)知道的上面事件傳遞機(jī)制后,我們就能理清楚我們的Button處理事件的邏輯了:
自定義Button繼承自系統(tǒng)的Button花鹅。
-
重寫 point(inside point: CGPoint, with event: UIEvent?) -> Bool 方法氧腰。在其中判斷當(dāng)前事件是否需要自身處理。
- 判斷點(diǎn)是否在自身button.imageView的frame范圍內(nèi)
- 得到點(diǎn)擊點(diǎn)在button.imageView中該點(diǎn)的顏色值
- 如果得到的色值中alpha小于閥值刨肃,則返回false
具體代碼可以查看JTShapedButton源碼古拴。