Повторное использование UITableView с различными типами моделей представлений - PullRequest
1 голос
/ 24 апреля 2020

Проблема:

У меня есть конкретный UITableView в моем проекте, и я копирую и вставляю код из этого UITableView и использую код для похожих экранов; этот подход к вещам действительно беспокоит меня.

Что я пробовал:

Я пытался найти способ обойти это. Например, я попытался использовать делегат и шаблон c, но продолжал получать ошибки.

Я попытался добавить объявление шаблона c в мой класс-координатор класса (см. CustomTableView ниже), однако я только что получили ошибки вроде: Protocol can only be used as a generic constraint because it has Self or associated type requirements, поэтому я просто переместил его обратно в структуру.

Я думал о том, чтобы просто объявить список моих моделей представлений в моей структуре UITableView как @ObservedObject и продолжаю с моим проектом. Тем не менее, это кажется легким путем, который заставляет меня думать, что это будет неправильный способ решить эту проблему.

Что мне не хватает?

Там Должен быть способ, которым я могу повторно использовать тот же самый tableView, просто передав ему связанный viewModel и никогда не требуя объявления ObservedObjects в структуре tableView.

У меня есть протокол, который выглядит следующим образом:

protocol CustomTableViewDelegate: ObservableObject {
    // ...
}

Все замыкания внутри этого протокола в основном являются клонами UITableViewDelegates методов. Почему я это делаю? Я делаю это так, что в любое время, когда любая из моих моделей представлений должна использовать мой пользовательский UITableView, я могу просто соответствовать этому делегату.

Как то, что я делаю здесь, в моей модели вида:

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

Я перепробовал все и, похоже, продолжаю ходить кругами , Я думал о том, чтобы иметь одну модель представления, которая ссылается на все мои другие модели представления, а затем передать ее в свой пользовательский вид таблицы, но я не осознаю, что, возможно, я что-то упустил, возможно, мои попытки решить все это были неудачными. aws.

Итак, как мне просто настроить мой CustomTableView, чтобы он мог работать с любым ViewModel, имеющим тип ObservableObject?

Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 24 апреля 2020

Здесь возможное решение. Протестировано с Xcode 11.4 / iOS 13.4

Если вы переместите / дублируете все обратные вызовы UITablView делегата / источника данных в модель представления, то на самом деле вам вообще не нужен координатор контекста, поэтому общие c сущности может быть как

// 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 , но для простоты демонстрации для направления выше должно хватить.

1 голос
/ 24 апреля 2020

Честно говоря, мне трудно понять, что вы пытаетесь сделать.

Я считаю, что эта ошибка: SwiftUI: ошибка представления UIRepresentable указывает на тип generi c, требующий Аргументы в <...>

просто означают, что в этой части:

  struct CustomTableView<T: CustomTableViewDelegate>: UIViewRepresentable {

    var viewModel: T

    class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {

      var customTableView: CustomTableView

вместо этого:

var customTableView: CustomTableView

у вас должно быть что-то вроде :

var customTableView: CustomTableView<SomeDelagateYouHaveSomewhere>

При использовании типа generi c убедитесь, что вы указали тип, который будет использоваться с ним. Хотя я действительно думаю, что это вообще не настоящая проблема.

По сути, у вас есть определение класса внутри структуры, которая ссылается на эту структуру ... Почему? Важно ли, чтобы этот класс можно было использовать только в рамках этой структуры?

Я думаю, что вам нужно просто наследовать класс UITableView, чтобы создать свой собственный, а затем использовать его или переопределить это когда необходимо.

Также обратите внимание на различия между структурой и классом (например, наследование). Вы можете найти полезную информацию здесь: https://learnappmaking.com/struct-vs-class-swift-how-to/

Этот ответ может быть не совсем тем, что вы ищете, но я надеюсь, что он поможет вам встать на правильный путь.

...