前言
前端頁面開發中,經常用到按鈕,按鈕的圖片是比較小的,但是需要擴大按鈕的點擊范圍.這時,我們一般會想到在響應鏈上動手
hitTest
/// 返回視圖層次結構(包括它自己)中包含指定點的接收方的最遠后代。
func hitTest(_ point: CGPoint,
with event: UIEvent?) -> UIView?
- 這個方法通過調用每個子視圖的point(inside:with:)方法來遍歷視圖層次結構,以確定哪個子視圖應該接收觸摸事件。如果point(inside:with:)返回true,則子視圖的層次結構將被類似地遍歷,直到找到包含指定點的最前面的視圖為止。如果一個視圖不包含這個點,它的視圖層次結構分支將被忽略。你很少需要自己調用這個方法,但是你可以覆蓋它來隱藏子視圖中的觸摸事件。
- 此方法忽略隱藏的、禁用用戶交互或alpha級別小于0.01的視圖對象。在確定命中時,此方法不考慮視圖的內容。因此,即使指定的點位于視圖內容的透明部分,視圖仍然可以被返回。
- 位于接收方邊界之外的點永遠不會被報告為命中,即使它們實際上位于接收方的子視圖之一。如果當前視圖的clipsToBounds屬性設置為false并且受影響的子視圖擴展超出了視圖的邊界,就會發生這種情況。
實現方案
- 擴大的點擊區域,我們不用去給按鈕添加上下左右單獨的屬性,直接用
UIEdgeInsets
即可,方便直觀,不容易設置錯誤 - 下面是實現代碼
open class EnlargeEdgeButton: UIButton {
open var enlargeEdge: UIEdgeInsets = .zero
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if alpha == 0 || isHidden == true || enlargeEdge == .zero {
return super.hitTest(point, with: event)
}
let rect = bounds.enlargeRect(edgeInsets: enlargeEdge)
return rect.contains(point) ? self : nil
}
}
extension CGRect {
public func enlargeRect(edgeInsets: UIEdgeInsets) -> CGRect {
return CGRect(x: minX - edgeInsets.left, y: minY - edgeInsets.top, width: width + (edgeInsets.left + edgeInsets.right), height: height + (edgeInsets.top + edgeInsets.bottom))
}
}
- 這是特別需要注意的是,按鈕的幾種特殊情況,需要做處理
-
alpha == 0
,透明度為0時,相當是按鈕是看不見的,那么這時,我們的期望時按鈕是隱藏狀態的,如果這里加了擴大按鈕點擊范圍的代碼,會導致雖然按鈕是隱藏的,但是點擊按鈕的放大區域,還是會響應事件,這會出現一個非常奇怪的bug,而且bug還不好找,本人在工作中就出現過這個失誤
2.isHidden == true
,同理,隱藏狀態時更不應該去擴大點擊范圍了
3.enlargeEdge == .zero
,擴大區域的edge為.zero,相當于是沒有加點擊范圍,那就可以直接調用父類方法,不做額外處理
分類 VS 子類化
- 個人覺得的分類的侵入性比較大,如果給
UIButton
添加分類屬性enlargeEdge
,導致任何繼承自UIButton
的類都有這個功能.有可能人家壓根就不想要這個功能,但是你本來就有這個功能 - 子類化的話,你想要這個功能,你就用這個類,或者繼承自這個類,能夠做到精準控制