代码之家  ›  专栏  ›  技术社区  ›  LondonGuy

使用不同的视图模型类型重用UITableView

  •  0
  • LondonGuy  · 技术社区  · 5 年前

    问题:

    我有个特别的 UITableView 在我的项目中,我发现自己复制并粘贴了这个代码

    我已经想办法解决这个问题。例如,我尝试使用委托和泛型,但一直出错。

    我尝试将泛型声明添加到我的协调器类(请参阅下面的CustomTableView)init函数中,但是我遇到了如下错误: Protocol can only be used as a generic constraint because it has Self or associated type requirements

    我想在我的 UITableView 的结构为 @ObservedObject 然后继续我的项目。然而,这似乎是一个简单的解决办法,这使我认为这将是一个错误的方法来解决这个问题。

    我错过了什么?

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

    protocol CustomTableViewDelegate: ObservableObject {
        // ...
    }
    

    UITableViewDelegates UITableView ,我可以简单地服从这个委托。

    就像我在视图模型中所做的:

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

    这是我的习惯 (我删除了一些函数以减少代码):

    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)
                }
        }
    }
    

    我什么都试过了,只是好像一直在绕圈子。我曾想过让一个视图模型引用所有其他视图模型,然后将其传递到我的自定义表视图中,但我没有意识到可能我遗漏了什么,也许我解决这些问题的尝试都有缺陷。

    提前谢谢。

    0 回复  |  直到 5 年前
        1
  •  2
  •   Asperi    5 年前

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

    如果你移动/复制所有 UITablView 委托/数据源回调到视图模型中,那么实际上您根本不需要上下文协调器,所以泛型实体可以

    // 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 ,但为了简单起见,以上方向的演示就足够了。

        2
  •  1
  •   yrk    5 年前

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

    我相信这个错误:

    简单地说,在本部分中:

      struct CustomTableView<T: CustomTableViewDelegate>: UIViewRepresentable {
    
        var viewModel: T
    
        class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {
    
          var customTableView: CustomTableView
    

    而不是这样:

    var customTableView: CustomTableView
    

    你应该有这样的东西:

    var customTableView: CustomTableView<SomeDelagateYouHaveSomewhere>
    

    虽然我真的认为这不是真正的问题。

    基本上,在结构内部有一个类定义,它引用这个结构。。。为什么? 这个类只能在这个结构的范围内使用,这很重要吗?

    我认为您只需要继承UITableView类来创建自定义类,然后在需要时使用它或重写它。

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

    这个答案并不完全是你想要的,但我希望它能帮助你走上正轨。