如何在UITableView扩展中为UIButton添加Target事件?
在UITableView扩展中为按钮添加点击事件的解决方案
我懂你现在的困扰——想在UITableView的空状态扩展里给按钮加上点击事件,但直接用addTarget会发现找不到合适的响应对象对吧?毕竟扩展里的self是UITableView实例,它本身不应该处理业务逻辑的点击事件。这里有两种实用的方案,帮你搞定这个需求:
方案一:用闭包轻量传递点击逻辑
这种方法最简洁,适合简单的点击场景,直接把点击后的逻辑以闭包的形式传入扩展方法:
import UIKit extension UITableView{ func setEmptyView(title: String, message: String, buttonAction: @escaping () -> Void) { // 创建按钮 let button = UIButton(type: .system) button.setTitle("+ Add New Documentation", for: .normal) button.titleLabel!.font = UIFont(name: "Roboto-Regular", size: 24) button.setTitleColor(UIColor(hex: "BDC86F"), for: .normal) button.backgroundColor = UIColor(hex: "506182") // 给按钮添加点击事件,用关联对象存储闭包 button.addTarget(self, action: #selector(emptyButtonTapped(_:)), for: .touchUpInside) objc_setAssociatedObject(button, &AssociatedKeys.emptyButtonAction, buttonAction, .OBJC_ASSOCIATION_COPY_NONATOMIC) // 补充emptyView创建、布局代码(示例) let emptyView = UIView(frame: self.bounds) button.translatesAutoresizingMaskIntoConstraints = false emptyView.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: emptyView.centerXAnchor), button.topAnchor.constraint(equalTo: emptyView.centerYAnchor, constant: 20) ]) self.backgroundView = emptyView } // 私有方法中转点击事件,调用闭包 @objc private func emptyButtonTapped(_ sender: UIButton) { if let action = objc_getAssociatedObject(sender, &AssociatedKeys.emptyButtonAction) as? () -> Void { action() } } } // 定义关联对象的Key(避免命名冲突) private enum AssociatedKeys { static var emptyButtonAction = "emptyButtonAction" }
使用方式(在你的ViewController里):
// 当需要显示空视图时调用 tableView.setEmptyView(title: "暂无文档", message: "点击添加新文档") { print("Hello World!") // 这里写你的点击逻辑,比如跳转到添加文档页面 }
方案二:用代理模式处理复杂交互
如果你的空视图按钮需要更复杂的交互逻辑,或者要在多个ViewController里复用,代理模式会更合适:
第一步:定义代理协议
protocol TableViewEmptyViewDelegate: AnyObject { func emptyViewAddButtonTapped() }
第二步:修改UITableView扩展
import UIKit extension UITableView{ // 用关联对象添加代理属性(扩展不能直接添加存储属性) weak var emptyViewDelegate: TableViewEmptyViewDelegate? { get { return objc_getAssociatedObject(self, &AssociatedKeys.emptyViewDelegate) as? TableViewEmptyViewDelegate } set { objc_setAssociatedObject(self, &AssociatedKeys.emptyViewDelegate, newValue, .OBJC_ASSOCIATION_ASSIGN) } } func setEmptyView(title: String, message: String) { let button = UIButton(type: .system) button.setTitle("+ Add New Documentation", for: .normal) button.titleLabel!.font = UIFont(name: "Roboto-Regular", size: 24) button.setTitleColor(UIColor(hex: "BDC86F"), for: .normal) button.backgroundColor = UIColor(hex: "506182") button.addTarget(self, action: #selector(emptyButtonTapped(_:)), for: .touchUpInside) // 补充emptyView的创建和布局代码(示例) let emptyView = UIView(frame: self.bounds) button.translatesAutoresizingMaskIntoConstraints = false emptyView.addSubview(button) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: emptyView.centerXAnchor), button.topAnchor.constraint(equalTo: emptyView.centerYAnchor, constant: 20) ]) self.backgroundView = emptyView } @objc private func emptyButtonTapped(_ sender: UIButton) { // 通知代理处理点击事件 emptyViewDelegate?.emptyViewAddButtonTapped() } } private enum AssociatedKeys { static var emptyViewDelegate = "emptyViewDelegate" }
使用方式(在你的ViewController里):
class YourViewController: UIViewController, TableViewEmptyViewDelegate { @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.emptyViewDelegate = self // 显示空视图 tableView.setEmptyView(title: "暂无文档", message: "点击添加新文档") } // 实现代理方法 func emptyViewAddButtonTapped() { print("Hello World!") // 处理按钮点击逻辑 } }
简单说下为什么不能直接用self作为target:UITableView是UI组件,不该承担业务逻辑的处理,点击事件应该由持有它的ViewController来负责,所以我们通过闭包或代理把逻辑传递进去,这样既保持了扩展的复用性,又让业务逻辑归属更清晰。
内容的提问来源于stack exchange,提问作者user12372692
相关产品推荐
相关产品推荐

