要实现的效果如下:
btn.addTarget(for: .touchUpInside) { (sender) in
print("button click!")
}
很多时候我们的的button点击事件就一行代码,而这一行代码还需要另写一个方法。然后用selector给button。这样:
当代码量增大,按钮的点击事件和按钮的定义就分散在代码的不同区域,给后期的代码维护造成不便。为了解决这个问题,我给UIButton扩展了一个用尾随闭包给按钮添加事件的方法。然后代码变成这样:
显然要代码集中了,也好好看了!
实现:
想法:给UIButton扩展一个方法带闭包参数的方法,在这个方法中去调用系统的addTraget方法,并把闭包保存起来,在addTraget中Selector对应的方法去执行闭包。
实现步骤:
1、在extension中给UIButton扩展一个带尾随闭包添加事件的方法:
func addTarget(for controlEvents: UIControlEvents,action:@escaping (UIButton)->())
{
}
在该方法中,要做两件事:
一、是给按钮添加响应事件,这个没啥可说的,直接调用系统API。
二、是把闭包保存起来,以便于在响应事件方法中去执行闭包。怎么保存呢,只能通过一个属性保存。extension扩展属性,用到runtime的关联属性方法。直接写一个闭包属性去保存事件闭包:
写是没问题,但一运行就会错误,通过实验,发现关联对象不支持基本数据类型,和闭包类型(具体原因我也没弄清楚,等搞清楚再修改过此处)。
鉴于这种情况,定义了一个结构体,在结构体中定义了一个闭包属性。关联一个该结构体类型的对象。问题便迎刃而解。
下面是全部扩展代码:
extension UIButton{
struct AssociatedClosureClass {
var eventClosure: (UIButton)->()
}
private struct AssociatedKeys {
static var eventClosureObj:AssociatedClosureClass?
}
private var eventClosureObj: AssociatedClosureClass{
set{
objc_setAssociatedObject(self, &AssociatedKeys.eventClosureObj, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
get{
return (objc_getAssociatedObject(self, &AssociatedKeys.eventClosureObj) as? AssociatedClosureClass)!
}
}
func addTarget(for controlEvents: UIControlEvents,action:@escaping (UIButton)->()) {
let eventObj = AssociatedClosureClass(eventClosure: action)
eventClosureObj = eventObj
addTarget(self, action: #selector(eventExcuate(_:)), for: controlEvents)
}
@objc private func eventExcuate(_ sender: UIButton){
eventClosureObj.eventClosure(sender)
}
}
同理,我们可以写监听通知,timer执行,segement切换的方法扩展。
如果你有更好的想法,或者能解答文中问题,希望看到你的留言。