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

Swift iOS-使用泛型重用相同的TableViewController,swift,uitableview,generics,reusability,Swift,Uitableview,Generics,Reusability,为了实现可与泛型重用的BaseTableViewController,我阅读了一些优秀的文章 它正在工作,下面是一个快速示例: if let mountains = viewModel?.model["mountain"] as? ResponseMountain { let mountainTableViewController = BaseTableViewController(items: mountains.result, config: { (cell, item) in

为了实现可与泛型重用的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)
 }
想象一下,我想显示一个来自我国的山脉的简单列表,当您选择一个山脉时,您将显示该山脉内所有可用地块的列表

这就是为什么我想重用同一个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
    }
}
最终类BaseTableViewController:UITableViewController{
可变项目:[项目]{
迪塞特{
tableView.reloadData()
}
}
let config:(UITableViewCell,项)->()
初始化(items:[Item],配置:@escaping(UITableViewCell,Item)->(),样式:UITableViewStyle){
self.items=项目
self.config=config
super.init(样式:style)
}
必需的初始化?(编码器aDecoder:NSCoder){
fatalError(“初始化(编码者:)尚未实现”)
}
重写func viewDidLoad(){
super.viewDidLoad()
}
重写func tableView(tableView:UITableView,numberofrowsinssection:Int)->Int{
返回项目。计数
}
重写func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
let cell=UITableViewCell(样式:。默认值,reuseIdentifier:“cellIdentifier”)
cell.textlab?.textColor=UIColor.white
let item=items[indexPath.row]
配置(单元格、项目)
返回单元
}
}
它高效、简单,适用于不同的用例


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

我会用处理
cellForRowAtIndexPath
的相同方法来执行此操作。使用类型
(Item)->()
将另一个闭包传递到
init
。然后在
didSelectRowAt
ad中调用该闭包并传递所选项目

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

我会像你处理
CellForRowatineXpath
一样处理它。使用类型
(Item)->()
将另一个闭包传递到
init
。然后在
didSelectRowAt
ad中调用该闭包并传递所选项目

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

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

首先,定义表数据项的协议以及与之配套的实现对象:

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

        }
    }
}
扩展UITableViewCell{ func配置(带项目:T){ 如果让项目=项目作为?表项目{ //基于TableItem类型进行配置 text标签?.text=item.title } } } 下面是基本tableview类的更新实现,用于处理此设置并显示另一个具有子列表中定义的列表的基本tableview控制器:

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)
    }
}
最终类BaseTableViewController:UITableViewController{
让cellId=“cellIdentifier”
变量项:[项]=[]{
迪塞特{
tableView.reloadData()
}
}
初始化(项:[项],样式:UITableViewStyle){
super.init(样式:style)
配置(使用:项)
}
必需的初始化?(编码器aDecoder:NSCoder){
fatalError(“初始化(编码者:)尚未实现”)
}
内部函数配置(带有项:[项]=[]){
tableView.register(UITableViewCell.self,forCellReuseIdentifier:cellId)
self.items=项目
}
重写func viewDidLoad(){
super.viewDidLoad()
}
重写func tableView(tableView:UITableView,numberofrowsinssection:Int)->Int{
返回项目。计数
}
重写func tableView(tableView:UITableView,cellForRowAt indexath:indexPath)->UITableViewCell{
let cell=tableView.dequeueReusableCell(标识符:cellId,for:indexath)
let item=items[indexPath.row]
cell.configure(使用:项)
返回单元
}
重写func tableView(tableView:UITableView,didSelectRowAt indexPath:indexPath){
取消选择行(at:indexPath,动画:true)
let item=items[indexPath.row]
guard let vc=getDetailController(项目:项目)else{
返回
}
当前(vc,动画:真,完成:无)
}
内部函数getDetailController(项:项)->UITableViewController{
guard let item=项目as?TableItem,let items=项目.items else{
归零
}
返回BaseTableViewController(项:项,样式:。普通)
}
}
我用这个设置代码测试了它

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)
}
func setupTableView(){
让项目=[
TableItemImp(标题:“阿尔卑斯山”,项目:[
TableItemImp(标题:“一”,项目:无)
]),
TableItemImp(标题:“阿巴拉契亚”,项目:[
TableItemImp(标题:“两个”,项目:无)
]),
TableItemImp(标题:“落基山脉”,项目:[
TableItemImp(标题:“三”,项目:无)
])
]
让tableVC=BaseTableViewController.init(项:项,样式:.plain)
当前(tableVC,动画:真,完成:无)
}

我在
viewdispect()
中的测试视图控制器中调用此代码,它为基本表视图控制器提供顶级列表。如果单击“Rockies”,它将显示另一个列表控制器,其中显示一个标题为“Three”的单元格。我可能会采取如下方法:

首先,定义表数据项的协议以及与之配套的实现对象:

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

        }
    }
}
扩展UITableViewCell{ func配置(带项目:T){ 如果让项目=项目为?