iOS - 使用泛型重用相同的 TableViewController

iOS - Reuse same TableViewController with Generics

我关注了一些很棒的文章以实现可与泛型一起重用的 BaseTableViewController。

它正在运行,这是一个简单的例子:

if let mountains = viewModel?.model["mountain"] as? ResponseMountain {
     let mountainTableViewController = BaseTableViewController(items: mountains.result, config: { (cell, item) in
         cell.textLabel?.text = item.name
      }, style: .plain)
      mountainTableViewController.title = "Mountains"
      self.navigationController?.pushViewController(mountainTableViewController, animated: true)
 }

想象一下,我想显示我所在国家/地区的山脉的简单列表,而当您 select 山脉时,您会显示该山脉内所有可用地块的列表。

这就是为什么我想重用相同的 TableViewController,但使用不同的数据。

我不知道如何用我的泛型实现来处理它。

这是我当前的实现:

final class BaseTableViewController<Item>: UITableViewController {

    var items: [Item] {
        didSet {
            tableView.reloadData()
        }
    }

    let config: (UITableViewCell, Item) -> ()

    init(items: [Item], config: @escaping (UITableViewCell, Item) -> (), style: UITableViewStyle) {
        self.items = items
        self.config = config
        super.init(style: style)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: "cellIdentifier")
        cell.textLabel?.textColor = UIColor.white
        let item = items[indexPath.row]
        config(cell, item)
        return cell
    }
}

它高效、简单,适用于不同的用例。

但是我如何使用泛型实现自定义 didSelectRowAt,以便使用我刚刚 select 编辑的行中的数据推送同一 BaseViewController 的实例?

我会按照你的方式去做 cellForRowAtIndexPath。将类型为 (Item) -> () 的另一个闭包传递给 init。然后在 didSelectRowAt 中调用该闭包并传递所选项目。

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let item = items[indexPath.row]
    didSelect(item)
}

我可能会采取这样的方法:

首先,为我们的 table 数据项定义协议和一个实现 object 以配合它:

protocol TableItem {
    var title: String { get set }
    var items: [TableItem]? { get set }
}

struct TableItemImp: TableItem {
    var title: String
    var items: [TableItem]?
}

接下来定义 UITableViewCell 的扩展来处理单元格配置:

extension UITableViewCell {

    func configure<T>(with item: T) {

        if let item = item as? TableItem {

            // configure based on TableItem type
            textLabel?.text = item.title

        }
    }
}

这是您的基础 table 视图 class 的更新实现,用于使用此设置并提供另一个基础 table 视图控制器,其中包含在子列表中定义的列表:

final class BaseTableViewController<Item>: UITableViewController {

    let cellId = "cellIdentifier"
    var items: [Item] = [] {
        didSet {
            tableView.reloadData()
        }
    }

    init(items: [Item], style: UITableViewStyle) {
        super.init(style: style)
        configure(with: items)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    internal func configure(with items: [Item] = []) {
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
        self.items = items
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return items.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
        let item = items[indexPath.row]
        cell.configure(with: item)
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let item = items[indexPath.row]
        guard let vc = getDetailController(item: item) else {
            return
        }
        present(vc, animated: true, completion: nil)
    }

    internal func getDetailController(item: Item) -> UITableViewController? {
        guard let item = item as? TableItem, let items = item.items else {
            return nil
        }

        return BaseTableViewController<TableItem>(items: items, style: .plain)
    }
}

我使用此设置代码对其进行了测试。

func setupTableView() {
    let items = [
        TableItemImp(title: "Alps", items: [
            TableItemImp(title: "One", items: nil)
            ]),
        TableItemImp(title: "Appalachian", items: [
            TableItemImp(title: "Two", items: nil)
            ]),
        TableItemImp(title: "Rockies", items: [
            TableItemImp(title: "Three", items: nil)
            ])
    ]
    let tableVC = BaseTableViewController<TableItem>.init(items: items, style: .plain)
    present(tableVC, animated: true, completion: nil)
}

我在 viewDidAppear() 的测试视图控制器中调用此代码,它向您的基础 table 视图控制器提供顶级列表。如果您单击 "Rockies",那么它将显示另一个列表控制器,显示一个标题为 "Three"

的单元格