在iOS开发时,经常需要对UIView、UILabel、UIImageView这类视图添加点击事件的捕获,而系统并不直接支持,因此常见的添加点击事件捕获的方式有2种:
1)在视图上盖一个UIButton,利用UIButton的addTarget方法,设置点击事件后的响应
2)在视图上设置一个 UITapGestureRecognizer 手势点击对象,在其action中设置点击事件后的响应
这2种方式都能实现功能,但是会有以下几个问题:
1)每次需要时创建覆盖的UIButton及手势对象使得代码不够精简,有很多冗长的代码(为了能捕获点击事件)混在UI视图的代码中
2)不管是UIButton还是UITapGestureRecognizer手势对象,其点击事件响应都需要在页面上另外增加触发函数,使得UI视图与触发响应事件的函数看起来不是那么直观、那么有联系,造成代码冗长
那么,能否针对不同的视图类型统一简便的添加点击事件的捕获呢?答案是肯定的。我们可以利用swift的泛型特征对UIView、UILabel、UIImageView等视图对象统一进行初始化并添加对其点击事件的捕获,还可以利用swift的尾随闭包使得对点击事件的回调直接以闭包的方式跟随在UI视图声明之后。
以下是封装的视图类代码:
class CommonView<T:UIView>:UIView{typealias swiftBlock = () -> Voidvar clickCallBack:swiftBlock! //点击事件blockvar strongSelf:T! //这个属性就是实际生成出来的view,可以是UIView的任何子类视图,如果要对其设置其他属性,直接在外面调用的地方访问该属性//不带点击回调的block参数,用户在外层需要调用clickCallBack属性来声明回调init(_ frame:CGRect){super.init(frame: frame)setup()}//直接带尾随闭包,用户在初始化时即可block调用点击回调init(_ frame:CGRect, _ completionHandler:@escaping swiftBlock){super.init(frame: frame)setup()clickCallBack = completionHandler}//设置初始化对象private func setup(){strongSelf = T.init()strongSelf.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)strongSelf.isUserInteractionEnabled = trueself.isUserInteractionEnabled = true//添加tapGuestureRecognizer手势(手势实际是加在底层的view上的,因为设置了上层的子视图传递响应,所以点击后底层的视图也就是本类也可以响应到)let tapGR = UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(Tap:)))self.addGestureRecognizer(tapGR)self.addSubview(strongSelf)}required init?(coder aDecoder: NSCoder) {fatalError("init(coder:) has not been implemented")}//手势处理函数func handleTapGesture(Tap:UITapGestureRecognizer) {clickCallBack()}}
以上我们定义了一个通用的视图类,并声明包含了一个继承UIView的对象strongSelf(T是占位类型),不管调用时指定的是UIView、UIImageView或者UILabel等,都可以在本类中进行初始化,并在其上添加点击手势对象,然后,点击后利用初始化本类时的尾随闭包即可将点击事件的回调设置到调用代码后面。
以下是调用代码:
//生成可得到点击回调的UIImageView视图(初始化需要cgrect参数),<>中指明视图类型(必须是继承UIView的),设置该视图上其他属性时,可通过其strongSelf属性进行设置,这里没有直接用尾随闭包,而是在初始化后访问clickCallBack来显式的声明回
let view1 = CommonView<UIImageView>(CGRect(x: 50.0, y: 50.0, width: 60.0, height: 60.0))
//通过strongSelf属性,设置其图片
view1.strongSelf.image = UIImage(named: "Common_head")//通过直接访问block来响应点击回调view1.clickCallBack = {print("点击了图片")}self.view.addSubview(view1)//使用尾随逃逸闭包在初始化的时候声明点击后的回调(UILabel视图)let view2 = CommonView<UILabel>(CGRect(x: 50.0, y: 140.0, width: 150, height: 30)){print("点击了标签")}//通过strongSelf属性,设置其标签其他属性view2.strongSelf.text = "我是标签,可以点击"self.view.addSubview(view2)//使用尾随逃逸闭包在初始化的时候声明点击后的回调(UIView视图)let view3 = CommonView<UIView>(CGRect(x: 50.0, y: 200.0, width: 60, height: 60)){print("点击了视图")}//通过strongSelf属性,设置其标签其他属性view3.strongSelf.backgroundColor = UIColor.blueself.view.addSubview(view3)
需要注意的是,使用闭包一定要注意是否会产生循环引用,如本例中,假如需要在点击事件后的block代码中调用本页面self上的某属性(只要用到了self对象都算),那么一定要在block顶部添加self为无主引用的声明 [unowned self] 才可在页面弹出时正确释放内存。
let view3 =CommonView<UIView>(CGRect(x: 50.0, y:200.0, width:60, height:60)){
[unownedself]in
print(self.titleName)
print("点击了视图")
}
以上是针对各种视图添加点击事件捕获的方案。