由于 UIControl 的消息事件是基于 action-target 机制的。所以在添加一个事件的处理代码时,通常会这么做:
- 将处理方法与控件关联。
ctl.addTarget(self, action: #selector(onTouchUpInside), for: .touchUpInside)
- 实现处理方法
@objc func onTouchUpInside(control: UIControl) {
print("事件的处理代码")
}
很显然,这种方法非常啰嗦,平白增加了诸多的代码量,非常不利于后期的调试和代码检查。为了简便,决定利用 Extension 为其增加基于 Block 的事件处理方式,核心代码如下(仅提供了 touchUpInside 事件的处理代码):
public typealias UIControlEventBlock = @convention(swift) (UIControl) -> ()
extension UIControl {
/// 定义 Runtime 时用到的键值。
fileprivate struct _RawPointers {
/// 在控件上按下并抬起
static let touchUpInside = UnsafeRawPointer(bitPattern: "automatic_touch_up_inside".hashValue)
/// 其他事件类似声明...
}
/// 在控件上按下并抬起时的处理代码。
public var touchUpInsideBlock: UIControlEventBlock? {
set {
if let pointer = _RawPointers.touchUpInside {
self.removeTarget(self, action: #selector(onTouchUpInside), for: .touchUpInside)
objc_setAssociatedObject(self, pointer, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
self.addTarget(self, action: #selector(onTouchUpInside), for: .touchUpInside)
}
}
get {
if let pointer = _RawPointers.touchUpInside {
return objc_getAssociatedObject(self, pointer) as? UIControlEventBlock
}
return nil
}
}
/// 在控件上按下并抬起时的处理函数
@objc private func onTouchUpInside() {
if let block = self.touchUpInsideBlock {
block(self)
}
}
}
使用时,同样的 touchUpInside 处理就可以被简化为:
ctl.touchUpInsideBlock = { control in
print("事件的处理代码")
}
网友评论