1.使用场景
在界面中,有些按钮和视图的区域比较小,用户难以准确的触摸到该区域;为了方便用户的操作,在不改变frame的前提下,扩大它的点击事件相应区域。
2.扩大它的点击事件相应区域方法
2.1在它的superView里,重写 hitTest: 方法
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let viewFrame = theView.frame.inset(by: UIEdgeInsets.init(top: -20, left: -20, bottom: -20, right: -20))
if viewFrame.contains(point) {
return theView
}
return super.hitTest(point, with: event)
}
以它 frame 进行扩大( UIEdgeInsets 相应的负数为扩大,正数为缩小)到目标 frame ,然后判断这个目标 frame 是否包括点击事件的点,如果是,就返回这个 theView,否则,就交给 super.hitTest: 去处理。
2.2重写它的point(inside point: CGPoint, with event: UIEvent?)方法
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let biggerFrame = self.bounds.inset(by: UIEdgeInsets.init(top: -20, left: -20, bottom: -20, right: -20))
return biggerFrame.contains(point)
}
这个方法是在我们无法确定 theView 的 superView 进行扩大点击事件,例如:UIBarButtonItem等。
2.3是这两种方法的混合,将需要扩展的数据添加到相应的分类或者 superView 的类中
///提供多个运行时的key
struct RuntimeKey {
static let buttonKey = UnsafeRawPointer.init(bitPattern: "BTNKey".hashValue)
}
///需要扩充的边距
var hitTestEdgeInsets: UIEdgeInsets? {
set {
objc_setAssociatedObject(self, RuntimeKey.buttonKey!, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_COPY)
}
get {
return objc_getAssociatedObject(self, RuntimeKey.buttonKey!) as? UIEdgeInsets ?? UIEdgeInsets.zero
}
}
///是否响应
open override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if (hitTestEdgeInsets! == UIEdgeInsets.zero) || !isEnabled || isHidden {
return super.point(inside: point, with: event)
}
else {
let expandArea = self.bounds.inset(by: hitTestEdgeInsets!)
return expandArea.contains(point)
}
}
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
return super.hitTest(point, with: event)
}
该例子是以UIButton的分类添加hitTestEdgeInsets的属性来达到按钮的点击相应区域的。
2.4注意的点
theView 扩展点击相应区域时,其扩展的区域不能超过 superView 的 frame ,否则不会相应改点击事件;如果需要响应点击事件,需要对其 superView 进行和 theView 进行同样的处理。