在事件響應(yīng)和傳遞這篇文章中,講了iOS中的事件響應(yīng)和傳遞,今天在做項(xiàng)目的時(shí)候,正好碰到了一個(gè)應(yīng)用的場景,因此記錄下來。
需求
首頁頭部需要添加這樣一個(gè)視圖

點(diǎn)擊左右兩個(gè)按鈕,底部的滑塊跟著滑動(dòng),同時(shí)也可以拖動(dòng)滑塊到選中的按鈕位置。
思路和問題
看到這個(gè)設(shè)計(jì)圖一開始的想法就是,左右兩個(gè)按鈕,底部一個(gè)滑塊視圖,然后到做的時(shí)候,發(fā)現(xiàn)如果要做手勢(shì)拖拽的話,那么由于層級(jí)關(guān)系,滑塊肯定是在兩個(gè)按鈕下面的,如果要觸發(fā)滑動(dòng)手勢(shì),勢(shì)必會(huì)被按鈕的點(diǎn)擊事件阻止。

通過重寫hitTest方法解決
首先判斷點(diǎn)擊點(diǎn)是位于左側(cè)按鈕區(qū)域還是右側(cè)按鈕區(qū)域,在根據(jù)按鈕的選中狀態(tài)來判斷。
初始狀態(tài)時(shí)左側(cè)按鈕選中,右側(cè)沒選中,同時(shí)滑塊位于左側(cè)。
如果觸摸點(diǎn)在右側(cè)按鈕區(qū)域,此時(shí)應(yīng)該走的流程是響應(yīng)右側(cè)按鈕事件。
如果觸摸點(diǎn)在左側(cè)區(qū)域,此時(shí)左側(cè)區(qū)域是選中狀態(tài),應(yīng)該響應(yīng)的是滑塊的拖動(dòng)事件。
依照這個(gè)思路,就有了下面的這段代碼
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let superHitPoint = self.convert(point, to: self.superview)
if superHitPoint.x <= self.frame.width {
if self.frame.minX > 0 { // 點(diǎn)擊左側(cè)按鈕,但是現(xiàn)在響應(yīng)的是右側(cè)按鈕,原樣傳遞上去
return super.hitTest(point, with: event)
}
if self.isSelected {
return strikeView
} else {
return super.hitTest(point, with: event)
}
} else {
if self.frame.minX == 0 { // 同理
return super.hitTest(point, with: event)
}
if self.isSelected {
return strikeView
} else {
return super.hitTest(point, with: event)
}
}
}
這里要注意的一點(diǎn)是,由于事件響應(yīng)鏈?zhǔn)窍到y(tǒng)會(huì)從后往前遍歷子視圖,我們點(diǎn)擊任何一個(gè)區(qū)域,兩個(gè)按鈕都會(huì)響應(yīng)hitTest方法,在判斷觸摸點(diǎn)的區(qū)域之后,還要判斷一下當(dāng)期響應(yīng)的按鈕是不是我們期望的按鈕,如果不是,則不作任何改動(dòng)。