У меня проблема при реализации MVC-паттерна на iOS. Я искал в Интернете, но, похоже, не нашел хорошего решения этой проблемы.
Многие UITableViewController
реализации кажутся довольно большими. Большинство примеров, которые я видел, позволяют UITableViewController
реализовать <UITableViewDelegate>
и <UITableViewDataSource>
. Эти реализации - большая причина, почему UITableViewController
становится большим. Одним из решений будет создание отдельных классов, которые реализуют <UITableViewDelegate>
и <UITableViewDataSource>
. Конечно, эти классы должны иметь ссылку на UITableViewController
. Есть ли недостатки в использовании этого решения? В общем, я думаю, что вы должны делегировать функциональность другим классам "Помощник" или подобным, используя шаблон делегата. Есть ли какие-то устоявшиеся способы решения этой проблемы?
Я не хочу, чтобы модель содержала слишком много функциональных возможностей или представлений. Я считаю, что логика действительно должна быть в классе контроллера, так как это один из краеугольных камней MVC-паттерна. Но главный вопрос:
Как следует разделить контроллер MVC-реализации на более мелкие управляемые части? (Относится к MVC в iOS в этом случае)
Возможно, существует общая схема решения этой проблемы, хотя я специально ищу решение для iOS. Пожалуйста, приведите пример хорошего шаблона для решения этой проблемы. Пожалуйста, предоставьте аргумент, почему ваше решение великолепно.
UITableViewController
механика кажется мне довольно странной, поэтому я могу коснуться проблемы. Я на самом деле рад использовать яMonoTouch
, потому чтоMonoTouch.Dialog
конкретно делает это , что намного легче работать с таблицами на прошивке. А пока мне любопытно, что другие, более знающие люди могли бы предложить здесь ...Ответы:
Я избегаю использования
UITableViewController
, так как в нем много обязанностей. Поэтому я отделяюUIViewController
подкласс от источника данных и делегата. Ответственность контроллера представления состоит в том, чтобы подготовить табличное представление, создать источник данных с данными и соединить эти вещи вместе. Изменение способа представления табличного представления может быть выполнено без изменения контроллера представления, и, действительно, один и тот же контроллер представления может использоваться для нескольких источников данных, которые следуют этому шаблону. Точно так же изменение рабочего процесса приложения означает изменения в контроллере представления, не беспокоясь о том, что происходит с таблицей.Я попытался отделяя
UITableViewDataSource
иUITableViewDelegate
протоколы в различные объекты, но это обычно заканчивается тем , что ложное разделение , как почти каждый метод на потребности делегата вырыть в источник данных (например , по выбору, потребности делегируют знать , какой объект представлен выбранный ряд). Таким образом, я получаю один объект, который является источником данных и делегатом. Этот объект всегда предоставляет метод, в-(id)tableView: (UITableView *)tableView representedObjectAtIndexPath: (NSIndexPath *)indexPath
котором аспекты источника данных и делегата должны знать, над чем они работают.Это моё разделение интересов на «уровне 0». Уровень 1 становится активным, если мне нужно представлять объекты разных видов в одном табличном представлении. В качестве примера представьте, что вам пришлось написать приложение «Контакты» - для одного контакта у вас могут быть строки, представляющие телефонные номера, другие строки, представляющие адреса, другие, представляющие адреса электронной почты, и так далее. Я хочу избежать этого подхода:
Два решения представили себя до сих пор. Одним из них является динамическое построение селектора:
При таком подходе вам не нужно редактировать эпическое
if()
дерево для поддержки нового типа - просто добавьте метод, который поддерживает новый класс. Это отличный подход, если это табличное представление является единственным, которое должно представлять эти объекты или должно представлять их особым образом. Если одни и те же объекты будут представлены в разных таблицах с разными источниками данных, этот подход не работает, поскольку методы создания ячеек требуют совместного использования источников данных - вы можете определить общий суперкласс, который предоставляет эти методы, или вы можете сделать это:Тогда в вашем классе источника данных:
Это означает, что любой источник данных, который должен отображать номера телефонов, адреса и т. Д., Может просто запросить любой объект, представленный для ячейки табличного представления. Самому источнику данных больше не нужно ничего знать об отображаемом объекте.
«Но подождите, - слышу я гипотетическое вмешательство собеседника, - разве это не нарушает MVC? Разве вы не помещаете детали вида в класс модели?»
Нет, это не нарушает MVC. В этом случае вы можете думать о категориях как о реализации Decorator ; так
PhoneNumber
это модель класса , ноPhoneNumber(TableViewRepresentation)
вид категории. Источник данных (объект контроллера) является посредником между моделью и представлением, поэтому архитектура MVC все еще сохраняется.Вы можете увидеть это использование категорий в качестве украшения в рамках Apple, тоже.
NSAttributedString
класс модели, содержащий текст и атрибуты AppKit предоставляет,NSAttributedString(AppKitAdditions)
а UIKit предоставляетNSAttributedString(NSStringDrawing)
категории декораторов, которые добавляют поведение рисования к этим классам моделей.источник
cellForPhotoAtIndexPath
методе источника данных, а затем вызываете соответствующий фабричный метод. Что, конечно, возможно, только если определенные классы предсказуемо занимают определенные строки. Думаю, ваша система генерации представлений о категориях на модели гораздо более элегантна на практике, хотя, возможно, это неортодоксальный подход к MVC! :)Люди склонны много упаковывать в UIViewController / UITableViewController.
Делегирование другому классу (не контроллеру представления) обычно работает нормально. Делегатам не обязательно нужна ссылка обратно на контроллер представления, так как все методы делегатов передают ссылку на
UITableView
, но им каким-то образом потребуется доступ к данным, для которых они делегируют.Несколько идей по реорганизации для сокращения длины:
если вы создаете ячейки табличного представления в коде, рассмотрите возможность их загрузки из файла пера или из раскадровки. Раскадровки допускают ячейки прототипа и статической таблицы - посмотрите эти возможности, если вы не знакомы
если ваши методы делегата содержат много операторов if (или switch), это классический признак того, что вы можете провести рефакторинг
Мне всегда было немного смешно, когда я
UITableViewDataSource
отвечал за получение правильного бита данных и настройку представления для его отображения. Хорошая точка рефакторинга может состоять в том, чтобы изменить вашcellForRowAtIndexPath
способ обработки данных, которые необходимо отобразить в ячейке, а затем делегировать создание представления ячейки другому делегату (например, makeCellViewDelegate
или подобному), который передается в соответствующем элементе данных.источник
Вот примерно то, что я сейчас делаю, когда сталкиваюсь с подобной проблемой:
Переместите связанные с данными операции в класс XXXDataSource (который наследуется от BaseDataSource: NSObject). BaseDataSource предоставляет несколько удобных методов, таких как
- (NSUInteger)rowsInSection:(NSUInteger)sectionNum;
, подкласс переопределяет метод загрузки данных (поскольку приложения, как правило, имеют какой-то метод загрузки автономного кэша, выглядит- (void)loadDataWithUpdateBlock:(LoadProgressBlock)dataLoadBlock completion:(LoadCompletionBlock)completionBlock;
так, что мы можем обновлять пользовательский интерфейс кэшированными данными, полученными в LoadProgressBlock, пока мы обновляем информацию из сети и в блоке завершения мы обновляем пользовательский интерфейс новыми данными и удаляем индикаторы прогресса, если таковые имеются). Эти классы НЕ соответствуютUITableViewDataSource
протоколу.В BaseTableViewController (что соответствует
UITableViewDataSource
иUITableViewDelegate
протоколы) у меня есть ссылка на BaseDataSource, который я создаю во время инициализации контроллера. ВUITableViewDataSource
части контроллера я просто возвращаю значения из dataSource (вроде- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [self.tableViewDataSource sectionsCount]; }
).Вот мой cellForRow в базовом классе (нет необходимости переопределять в подклассах):
configureCell должен быть переопределен подклассами, а createCell возвращает UITableViewCell, поэтому, если вам нужна пользовательская ячейка, переопределите ее тоже.
После того, как базовые вещи сконфигурированы (фактически, в первом проекте, который использует такую схему, после этого эта часть может быть повторно использована), что остается для
BaseTableViewController
подклассов:Переопределить configureCell (это обычно трансформируется в запрос dataSource для объекта для пути индекса и подачу его в configureWithXXX: метод ячейки или получение представления UITableViewCell объекта, как в ответе user4051)
Переопределить didSelectRowAtIndexPath: (очевидно)
Напишите подкласс BaseDataSource, который позаботится о работе с необходимой частью Model (предположим, что есть 2 класса
Account
иLanguage
, таким образом, подклассами будут AccountDataSource и LanguageDataSource).И это все для части просмотра таблицы. Я могу опубликовать код на GitHub, если это необходимо.
Изменить: некоторые рекомендации можно найти по адресу http://www.objc.io/issue-1/lighter-view-controllers.html (который имеет ссылку на этот вопрос) и сопутствующей статье о tableviewcontrollers.
источник
Мой взгляд на это заключается в том, что модели необходимо предоставить массив объектов, которые называются ViewModel или viewData, инкапсулированными в cellConfigurator. CellConfigurator содержит CellInfo, необходимый для его удаления и настройки ячейки. это дает ячейке некоторые данные, чтобы ячейка могла настроить себя. это также работает с разделом, если вы добавите некоторый объект SectionConfigurator, который содержит CellConfigurators. Я начал использовать это некоторое время назад, просто предоставив ячейке viewData, и ViewController занялся снятием очереди с ячейки. но я прочитал статью, которая указала на это репозиторий gitHub.
https://github.com/fastred/ConfigurableTableViewController
это может изменить способ, которым вы подходите к этому.
источник
Недавно я написал статью о том, как реализовать делегаты и источники данных для UITableView: http://gosuwachu.gitlab.io/2014/01/12/uitableview-controller/
Основная идея состоит в том, чтобы разделить обязанности на отдельные классы, такие как фабрика ячеек, фабрика секций, и предоставить некоторый общий интерфейс для модели, которую будет отображать UITableView. Диаграмма ниже объясняет все это:
источник
Следование принципам SOLID решит любые подобные проблемы.
Если вы хотите , чтобы ваши классы , чтобы иметь только один ответственность, вы должны определить отдельные
DataSource
иDelegate
классы и просто вводить их вtableView
владелец (может бытьUITableViewController
илиUIViewController
или что - нибудь еще). Вот как вы преодолеваете разделение интересов .Но если вы просто хотите иметь чистый и читаемый код и хотите избавиться от этого массивного файла viewController, и вы находитесь в Swif , вы можете использовать
extension
s для этого. Расширения одного класса могут быть записаны в разных файлах, и все они имеют доступ друг к другу. Но это не совсем решает проблему SoC, как я уже упоминал.источник