循环生成UIButton与UIView后,如何在按钮点击事件传递对应UIView
嘿,这个场景在iOS开发里太常见了,循环生成的按钮要对应到视图,光靠tag确实不够灵活,给你分享几个亲测好用的方案:
方案一:用关联对象给UIButton绑定对应UIView
这是比较灵活的方式,利用Objective-C运行时特性,给UIButton扩展一个自定义属性来存储对应的UIView:
首先导入运行时框架:
import ObjectiveC
给UIButton添加关联属性:
private var associatedViewKey = 0 extension UIButton { var linkedView: UIView? { get { return objc_getAssociatedObject(self, &associatedViewKey) as? UIView } set { objc_setAssociatedObject(self, &associatedViewKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
修改循环代码,把生成的myView绑定到按钮上:
for (key, value) in myStringsArray { let myButton = UIButton() let myView = UIView() // 注意你原代码里写的是panelButton,应该是myButton哦 myButton.tag = value myButton.linkedView = myView // 绑定对应的视图 myButton.addTarget(self, action: #selector(onMyButtonClick), for: .touchUpInside) // 记得把button和view添加到父视图中 }
最后修改点击事件,直接获取绑定的视图并切换隐藏状态:
@objc func onMyButtonClick(sender: UIButton) { sender.linkedView?.isHidden = !(sender.linkedView?.isHidden ?? false) }
方案二:用闭包实现按钮点击(更Swift风格的方式)
如果不想用Selector和关联对象,可以封装一个支持闭包点击的UIButton,循环里直接处理对应视图的逻辑:
先扩展UIButton添加闭包点击能力:
extension UIButton { private struct AssociatedKeys { static var tapClosure = "tapClosure" } typealias TapHandler = () -> Void private var tapHandler: TapHandler? { get { return objc_getAssociatedObject(self, &AssociatedKeys.tapClosure) as? TapHandler } set { objc_setAssociatedObject(self, &AssociatedKeys.tapClosure, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } func addTapHandler(handler: @escaping TapHandler) { tapHandler = handler addTarget(self, action: #selector(handleTap), for: .touchUpInside) } @objc private func handleTap() { tapHandler?() } }
循环里直接给按钮绑定闭包,直接操作当前的myView:
for (key, value) in myStringsArray { let myButton = UIButton() let myView = UIView() // 闭包里直接操作当前循环的myView,逻辑更直观 myButton.addTapHandler { [weak self] in myView.isHidden = !myView.isHidden // 如果需要访问self的属性,记得用weak self避免循环引用 } // 添加到父视图... }
这种方式不用单独写Selector方法,逻辑都集中在循环里,还能避免tag冲突的问题,非常推荐。
方案三:用字典存储tag和UIView的映射关系
如果还是想依赖tag实现,可以把生成的UIView存到字典里,用tag作为key:
首先定义一个字典属性:
var viewTagMap = [Int: UIView]()
循环里把View存入字典:
for (key, value) in myStringsArray { let myButton = UIButton() let myView = UIView() let tag = value // 假设value是Int类型 myButton.tag = tag viewTagMap[tag] = myView // 存储tag与视图的映射 myButton.addTarget(self, action: #selector(onMyButtonClick), for: .touchUpInside) // 添加到父视图... }
点击事件里通过tag取出对应View:
@objc func onMyButtonClick(sender: UIButton) { if let targetView = viewTagMap[sender.tag] { targetView.isHidden = !targetView.isHidden } }
这种方式要注意tag必须唯一,如果myStringsArray里的value有重复,会覆盖之前的视图,使用前要确保tag的唯一性。
以上几种方案都能完美解决你的需求,个人更推荐方案二,更贴合Swift的编程习惯,也能避开很多潜在的坑~
内容的提问来源于stack exchange,提问作者Dylan
相关产品推荐
相关产品推荐

