Swift UI Delegate方法优化:解决多UIViewController中UITableView代理代码重复问题
优化方案
你可以通过「抽离独立的代理数据源对象」的组合模式来消除重复代码,相比继承基类的方案灵活度更高,后续自定义扩展也更方便,具体实现步骤如下:
步骤1:定义通用的TableView代理处理类
首先创建一个泛型的代理类,兼容不同的Model和Cell类型:
import UIKit class TableViewDataSourceDelegate<T, Cell: UITableViewCell>: NSObject, UITableViewDataSource, UITableViewDelegate { // 存储数据源 var items: [T] = [] // Cell复用ID private let cellIdentifier: String // Cell配置回调 private let configureCell: (Cell, T, IndexPath) -> Void // 点击Cell回调 private let didSelectItem: (T, IndexPath) -> Void // 初始化方法 init(cellIdentifier: String, configureCell: @escaping (Cell, T, IndexPath) -> Void, didSelectItem: @escaping (T, IndexPath) -> Void) { self.cellIdentifier = cellIdentifier self.configureCell = configureCell self.didSelectItem = didSelectItem super.init() } // MARK: - UITableViewDataSource func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? Cell else { return UITableViewCell() } let item = items[indexPath.row] configureCell(cell, item, indexPath) return cell } // MARK: - UITableViewDelegate func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) let item = items[indexPath.row] didSelectItem(item, indexPath) } }
步骤2:在ViewController中使用
改造后的ViewController代码会大幅精简,不需要再实现重复的代理方法:
class FirstViewController: UIViewController { @IBOutlet weak var tableView: UITableView! var userArray = [UserModel]() // 声明代理数据源对象 private var tableViewHandler: TableViewDataSourceDelegate<UserModel, UserTableViewCell>! override func viewDidLoad() { super.viewDidLoad() // 注册Cell tableView.register(UserTableViewCell.self, forCellReuseIdentifier: "Cell01") // 初始化代理对象,传入配置和点击回调 tableViewHandler = TableViewDataSourceDelegate( cellIdentifier: "Cell01", configureCell: { cell, user, indexPath in // 在这里配置Cell内容 cell.nameLabel.text = user.name cell.avatarImageView.image = UIImage(named: user.avatar) }, didSelectItem: { [weak self] user, indexPath in // 在这里处理点击逻辑 self?.navigateToUserDetail(user: user) } ) // 把tableView的代理和数据源绑定到自定义的handler上 tableView.delegate = tableViewHandler tableView.dataSource = tableViewHandler } // 获取到数据后刷新列表 func reloadUserData(_ users: [UserModel]) { tableViewHandler.items = users tableView.reloadData() } private func navigateToUserDetail(user: UserModel) { // 跳转逻辑 } } // 第二个商品列表控制器同理,只需要替换泛型类型即可 class SecondViewController: UIViewController { @IBOutlet weak var tableView: UITableView! var productArray = [ProductModel]() private var tableViewHandler: TableViewDataSourceDelegate<ProductModel, ProductTableViewCell>! override func viewDidLoad() { super.viewDidLoad() tableView.register(ProductTableViewCell.self, forCellReuseIdentifier: "Cell01") tableViewHandler = TableViewDataSourceDelegate( cellIdentifier: "Cell01", configureCell: { cell, product, indexPath in cell.titleLabel.text = product.title cell.priceLabel.text = "¥\(product.price)" }, didSelectItem: { [weak self] product, indexPath in self?.navigateToProductDetail(product: product) } ) tableView.delegate = tableViewHandler tableView.dataSource = tableViewHandler } }
扩展说明
- 如果需要实现更多代理方法(比如自定义行高、区头区尾),只需要在
TableViewDataSourceDelegate类中补充对应的实现即可,所有用到的控制器都能复用 - 如果个别控制器需要特殊的代理逻辑,不需要修改通用类,单独给当前控制器实现对应的代理方法即可,不会影响其他页面
- 这种组合方式也支持同一个控制器内多个TableView的场景,只需要创建多个对应的handler对象分别绑定即可
内容的提问来源于stack exchange,提问作者Muhammad Haroon Iqbal
相关产品推荐
相关产品推荐

