繁体   English   中英

以不同的视图重用 UITableView 类型 model 类型

[英]Reusing a UITableView with different view model types

问题:

我的项目中有一个特定的UITableView ,我发现自己从这个UITableView复制和粘贴代码并使用类似屏幕的代码; 这种处理事情的方法真的让我很烦。

我试过的:

我试图找到解决这个问题的方法。 例如,我尝试使用委托和泛型,但不断出错。

我尝试将通用声明添加到我的 Coordinator class(请参见下面的 CustomTableView) init function,但是,我遇到了如下错误: Protocol can only be used as a generic constraint because it has Self or associated type requirements ,所以我只是将其移回到结构。

我考虑过在我的UITableView的结构中将我的视图模型列表声明为@ObservedObject并继续我的项目。 然而,这似乎是一个简单的方法,这让我认为这将是解决这个问题的错误方法。

我错过了什么?

必须有一种方法可以重用相同的 tableView,只需传入其关联的 viewModel,而无需在 tableView 结构中声明 ObservedObjects。

我有一个看起来像这样的协议:

protocol CustomTableViewDelegate: ObservableObject {
    // ...
}

该协议中的所有闭包基本上都是UITableViewDelegates方法的克隆。 我为什么要这样做? 我这样做是为了在任何时候我的任何视图模型都需要使用我的自定义UITableView ,我可以简单地遵循这个委托。

就像我在 model 中所做的那样:

class CustomViewModel: ObservableObject, CustomTableViewDelegate {
   // ...
}

这是我的自定义UITableView (我删除了一些功能以减少代码):

struct CustomTableView<T: CustomTableViewDelegate>: UIViewRepresentable {

    var viewModel: T

    class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {

        var customTableView: CustomTableView

        init(_ customTableView: CustomTableView) {
            self.customTableView = customTableView
        }

        func numberOfSections(in tableView: UITableView) -> Int {
            self.customTableView.viewModel.numberOfSections(in: tableView)
        }

        func makeCoordinator() -> CustomTableView.Coordinator {
            Coordinator(self)
        }

        func makeUIView(context: Context) -> UITableView {
            let coordinator = context.coordinator
            return context.coordinator.customTableView.viewModel.makeUIView(coordinator: coordinator)
        }

        func updateUIView(_ uiView: UITableView, context: Context) {
            context.coordinator.customTableView.viewModel.updateUIView(uiView, coordinator: context.coordinator)
        }
    }
}

在我的主要观点中:

struct MyMainView: View {
    @EnvironmentObject var customViewModel: CustomViewModel

    var body: some View {

        return

            VStack {
                CustomTableView<CustomViewModel>(viewModel: customViewModel)
            }
    }
}

我已经尝试了一切,但似乎一直在绕圈子。 我想过有一个视图 model 引用我的所有其他视图模型,然后将其传递到我的自定义表格视图中,但我没有意识到也许我遗漏了一些东西,也许我解决这一切的尝试都有缺陷.

那么,如何简单地设置我的 CustomTableView 以便它可以与任何具有 ObservableObject 类型的 ViewModel 一起使用?

提前致谢。

这是可能的解决方案。 使用 Xcode 11.4 / iOS 13.4 测试

如果您将所有UITablView委托/数据源回调移动/复制到视图 model 中,那么实际上您根本不需要上下文协调器,因此通用实体可以是

// generic table view model protocol
protocol CustomTableViewModel: UITableViewDataSource, UITableViewDelegate {
    func configure(tableView: UITableView)
}

// generic table view that depends only on generic view model
struct CustomTableView<ViewModel:ObservableObject & CustomTableViewModel>: UIViewRepresentable {
    @ObservedObject var viewModel: ViewModel

    func makeUIView(context: Context) -> UITableView {
        let tableView = UITableView()
        viewModel.configure(tableView: tableView)
        return tableView
    }

    func updateUIView(_ tableView: UITableView, context: Context) {
        tableView.reloadData()
    }
}

这是使用示例

// some specific model
class MyViewModel: NSObject, ObservableObject, CustomTableViewModel {
    let items = ["one", "two", "three"]
    let cellIdentifier = "MyCell"

    func configure(tableView: UITableView) {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: cellIdentifier)
        tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
    }

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

    func numberOfRows(in section: Int) -> Int { 1 }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableViewCell
        cell.textLabel?.text = items[indexPath.row]
        return cell
    }
}

struct MyView: View {
    @EnvironmentObject var myViewModel: MyViewModel

    var body: some View {
        CustomTableView(viewModel: myViewModel)
    }
}

注意:实际上,下一个分解步骤可能是将Presenter概念与ViewModel分离,但为了简单起见,上述方向的演示应该足够了。

备份

老实说,我很难理解你想做什么。

我相信这个错误: SwiftUI: UIRepresentable view error states a reference to a generic type requires arguments in <...>

只是意味着在这部分:

  struct CustomTableView<T: CustomTableViewDelegate>: UIViewRepresentable {

    var viewModel: T

    class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {

      var customTableView: CustomTableView

而不是这个:

var customTableView: CustomTableView

你应该有类似的东西:

var customTableView: CustomTableView<SomeDelagateYouHaveSomewhere>

使用泛型类型时,只需确保提供要使用的类型。 尽管我真的认为这完全不是真正的问题。

基本上你有一个 class 定义在引用这个结构的结构内部......为什么? 这个 class 只能在这个结构的 scope 中使用重要吗?

我认为您想要的只是 UITableView class 的 inheritance 来创建您的自定义,然后在需要时使用或覆盖它。

另请注意 struct 和 class 之间的区别(如继承)。 你可以在这里找到一些有用的信息: https://learnappmaking.com/struct-vs-class-swift-how-to/

这个答案可能不是您正在寻找的,但我希望它可以帮助您走上正轨。

它实际上比你想象的要容易。 表视图只关心数据源,而不关心它来自哪里。 只需创建一个 class 来实现 UITableViewDataSource,您也可以对 UITableViewDelegate 执行相同的操作。 将数据源和委托分配给表格视图的数据源和委托,仅此而已。 您可以在组合根中执行此操作,从那里实例化视图控制器并访问表视图。 这也是隔离测试所有内容的好方法。

class MyDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
private var dataSource: [String] = []

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

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
    
    return cell
}


func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // 
}

}

你所要做的就是:

let datasourceProvider = MyDataSource()
let vc = SomeUIViewController()
vc.tableView.datasource = datasourceProvider
vc.tableView.delegate = datasourceProvider

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM