Создание модели представления для каждого UITableViewCell

Я застрял в дизайнерском решении с созданием моделей представлений для ячеек таблицы. Данные для каждой ячейки предоставляются классом источника данных (имеет массив Contacts). В MVVM только модель просмотра может разговаривать с моделью, но не имеет смысла размещать источник данных в модели вида, поскольку она позволяет получать доступ к данным для всех ячеек, также неправильно помещать источник данных в контроллер поскольку он не должен иметь ссылки на данные. Есть и другие ключевые моменты:

  • Каждая ячейка должна иметь собственный экземпляр модели представления, а не общий.
  • cellForRowAtindexPath не следует размещать в представлении-модели, потому что он не должен содержать ссылок на пользовательский интерфейс.
  • View/ViewController view-model не должна взаимодействовать с моделью ячеек

Какой правильный способ "вставить" источник данных для ячеек в отношениях MVVM? Спасибо.

Ответы

Ответ 1

Позвольте мне начать с некоторой теории. MVVM является специализацией Модель представления (или Модель приложения) для Microsoft Silverlight и WPF. Основные идеи, лежащие в основе этого архитектурного шаблона пользовательского интерфейса:

  • Часть просмотра является единственной, которая зависит от структуры GUI. Это означает, что для iOS контроллер представления является частью представления.
  • В представлении может отображаться только модель представления. Никогда.
  • Модель просмотра содержит состояние представления. Это состояние предлагается для представления через свойства модели представления. Эти свойства содержат не только значение меток, но и другую информацию, связанную с просмотром, например, если включена кнопка сохранения или цвет для рейтинга. Но информация государства должна быть независимой от пользовательского интерфейса. Таким образом, в случае iOS свойство для цвета должно быть перечислением, например, вместо UIColor.
  • Модель просмотра также предоставляет методы, которые будут учитывать действия пользовательского интерфейса. Эти действия будут обсуждаться с моделью, но они никогда не изменят состояние представления, напрямую связанного с данными. Вместо этого он разговаривает с моделью и просит внести необходимые изменения.
  • Модель должна быть автономной, т.е. вы должны иметь возможность использовать тот же код для модели для приложения командной строки и интерфейса интерфейса. Он позаботится обо всей бизнес-логике.
  • Модель не знает о модели представления. Таким образом, изменения в модели представления распространяются через механизм наблюдения. Для iOS и модели с обычными подклассами NSObject или даже Core Data для этого может использоваться KVO (также для Swift).
  • Когда модель представления узнает об изменениях в модели, она должна обновить состояние, которое оно хранит (если вы используете типы значений, тогда она должна создать обновленную версию и заменить ее).
  • Модель представления не знает о представлении. В своей первоначальной концепции он использует привязку данных, недоступную для iOS. Таким образом, изменения в модели представления распространяются через механизм наблюдения. Вы также можете использовать KVO здесь, или, как вы упомянули в вопросе, будет делать простой шаблон делегирования, даже лучше, если он будет объединен с наблюдателями свойств Swift. Некоторые люди предпочитают реактивные рамки, такие как RxSwift, ReactiveCocoa или даже Swift Bond.

Преимущества, как вы упомянули:

  • Лучшее разделение проблем.
  • Независимость от пользовательского интерфейса: упрощение миграции на другие пользовательские интерфейсы.
  • Лучшая тестируемость из-за разделения проблем и развязанного характера кода.

Итак, вернувшись к вашему вопросу, реализация протокола UITableViewDataSource принадлежит к части представления архитектуры из-за ее зависимостей от структуры пользовательского интерфейса. Обратите внимание, что для использования этого протокола в коде этот файл должен импортировать UIKit. Также методы, подобные tableView(:cellForRowAt:), которые возвращают представление, сильно зависят от UIKit.

Затем ваш массив Contacts, который действительно является вашей моделью, не может быть обработан или запрошен через представление (источник данных или иначе). Вместо этого вы передаете модель представления в контроллер табличного представления, который в простейшем случае имеет два свойства (я рекомендую их хранить, а не вычисленные свойства). Одним из них является количество разделов, а другое - количество строк в секции:

var numberOfSections: Int = 0
var rowsPerSection: [Int] = []

Ответ 2

Если у вас нет конкретной проблемы, которая решена с помощью Model-View-ViewModel, то попытка принять ее только для "лучших практик" в конечном итоге приведет к множеству ненужной сложности.

Ваш источник данных отвечает за заполнение таблицы. Ничто иное, как ваш источник данных, не нуждается в ссылке на contacts, так как он обновит вашу таблицу этими данными.

View Models только вступают в игру, когда вам нужно выполнять сложные взаимодействия и обновления интерфейса. VM отвечает за инкапсуляцию состояния вашего представления, например...

  • Значения текстовых полей
  • Какие флажки/переключатели выбраны
  • Цвета элементов
  • Анимационная логика
  • Зависимости между элементами пользовательского интерфейса

Когда изменения внесены в ваш вид, ваш View Model отвечает за внесение обновлений в ваш Model (при необходимости), чтобы отразить изменения, внесенные в этот Model через пользовательский интерфейс.

При всем том, что в моделях IOS модели View не имеют смысла, потому что IOS использует View Controllers в методологии проектирования под названием MVC (Model-View-Controller)