Я избегаю использования UITableViewController
, так как в нем много обязанностей. Поэтому я отделяю UIViewController
подкласс от источника данных и делегата. Ответственность контроллера представления состоит в том, чтобы подготовить табличное представление, создать источник данных с данными и соединить эти вещи вместе. Изменение способа представления табличного представления может быть выполнено без изменения контроллера представления, и, действительно, один и тот же контроллер представления может использоваться для нескольких источников данных, которые следуют этому шаблону. Точно так же изменение рабочего процесса приложения означает изменения в контроллере представления, не беспокоясь о том, что происходит с таблицей.
Я попытался отделяя UITableViewDataSource
и UITableViewDelegate
протоколы в различные объекты, но это обычно заканчивается тем , что ложное разделение , как почти каждый метод на потребности делегата вырыть в источник данных (например , по выбору, потребности делегируют знать , какой объект представлен выбранный ряд). Таким образом, я получаю один объект, который является источником данных и делегатом. Этот объект всегда предоставляет метод, в -(id)tableView: (UITableView *)tableView representedObjectAtIndexPath: (NSIndexPath *)indexPath
котором аспекты источника данных и делегата должны знать, над чем они работают.
Это моё разделение интересов на «уровне 0». Уровень 1 становится активным, если мне нужно представлять объекты разных видов в одном табличном представлении. В качестве примера представьте, что вам пришлось написать приложение «Контакты» - для одного контакта у вас могут быть строки, представляющие телефонные номера, другие строки, представляющие адреса, другие, представляющие адреса электронной почты, и так далее. Я хочу избежать этого подхода:
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
id object = [self tableView: tableView representedObjectAtIndexPath: indexPath];
if ([object isKindOfClass: [PhoneNumber class]]) {
//configure phone number cell
}
else if …
}
Два решения представили себя до сих пор. Одним из них является динамическое построение селектора:
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
id object = [self tableView: tableView representedObjectAtIndexPath: indexPath];
NSString *cellSelectorName = [NSString stringWithFormat: @"tableView:cellFor%@AtIndexPath:", [object class]];
SEL cellSelector = NSSelectorFromString(cellSelectorName);
return [self performSelector: cellSelector withObject: tableView withObject: object];
}
- (UITableViewCell *)tableView: (UITableView *)tableView cellForPhoneNumberAtIndexPath: (NSIndexPath *)indexPath {
// configure phone number cell
}
При таком подходе вам не нужно редактировать эпическое if()
дерево для поддержки нового типа - просто добавьте метод, который поддерживает новый класс. Это отличный подход, если это табличное представление является единственным, которое должно представлять эти объекты или должно представлять их особым образом. Если одни и те же объекты будут представлены в разных таблицах с разными источниками данных, этот подход не работает, поскольку методы создания ячеек требуют совместного использования источников данных - вы можете определить общий суперкласс, который предоставляет эти методы, или вы можете сделать это:
@interface PhoneNumber (TableViewRepresentation)
- (UITableViewCell *)tableView: (UITableView *)tableView representationAsCellForRowAtIndexPath: (NSIndexPath *)indexPath;
@end
@interface Address (TableViewRepresentation)
//more of the same…
@end
Тогда в вашем классе источника данных:
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
id object = [self tableView: tableView representedObjectAtIndexPath: indexPath];
return [object tableView: tableView representationAsCellForRowAtIndexPath: indexPath];
}
Это означает, что любой источник данных, который должен отображать номера телефонов, адреса и т. Д., Может просто запросить любой объект, представленный для ячейки табличного представления. Самому источнику данных больше не нужно ничего знать об отображаемом объекте.
«Но подождите, - слышу я гипотетическое вмешательство собеседника, - разве это не нарушает MVC? Разве вы не помещаете детали вида в класс модели?»
Нет, это не нарушает MVC. В этом случае вы можете думать о категориях как о реализации Decorator ; так PhoneNumber
это модель класса , но PhoneNumber(TableViewRepresentation)
вид категории. Источник данных (объект контроллера) является посредником между моделью и представлением, поэтому архитектура MVC все еще сохраняется.
Вы можете увидеть это использование категорий в качестве украшения в рамках Apple, тоже. NSAttributedString
класс модели, содержащий текст и атрибуты AppKit предоставляет, NSAttributedString(AppKitAdditions)
а UIKit предоставляет NSAttributedString(NSStringDrawing)
категории декораторов, которые добавляют поведение рисования к этим классам моделей.