Ich vermeide es zu benutzen UITableViewController
, da es viele Verantwortlichkeiten in ein einzelnes Objekt legt. Deshalb trenne ich die UIViewController
Unterklasse von der Datenquelle und delegiere. Der View Controller ist dafür verantwortlich, die Tabellenansicht vorzubereiten, eine Datenquelle mit Daten zu erstellen und diese Dinge miteinander zu verknüpfen. Die Darstellung der Tabellenansicht kann geändert werden, ohne dass der Ansichts-Controller geändert werden muss. In der Tat kann derselbe Ansichts-Controller für mehrere Datenquellen verwendet werden, die alle diesem Muster folgen. In ähnlicher Weise bedeutet das Ändern des App-Workflows Änderungen am Ansichts-Controller, ohne sich Gedanken darüber zu machen, was mit der Tabelle geschieht.
Ich habe versucht, das UITableViewDataSource
und UITableViewDelegate
-Protokoll in verschiedene Objekte zu unterteilen, aber das führt normalerweise zu einer falschen Aufteilung, da fast jede Methode des Delegaten in die Datenquelle eintauchen muss (z. B. muss der Delegat bei der Auswahl wissen, welches Objekt durch das dargestellt wird ausgewählte Zeile). Am Ende habe ich ein einzelnes Objekt, das sowohl Datenquelle als auch Delegat ist. Dieses Objekt stellt immer eine Methode bereit, mit -(id)tableView: (UITableView *)tableView representedObjectAtIndexPath: (NSIndexPath *)indexPath
der sowohl die Datenquelle als auch die Delegierungsaspekte wissen müssen, woran sie arbeiten.
Das ist meine "Level 0" Trennung von Bedenken. Stufe 1 wird aktiviert, wenn ich Objekte unterschiedlicher Art in derselben Tabellenansicht darstellen muss. Stellen Sie sich zum Beispiel vor, Sie müssten die Kontakte-App schreiben - für einen einzelnen Kontakt stehen möglicherweise Zeilen für Telefonnummern, andere Zeilen für Adressen, andere für E-Mail-Adressen usw. Ich möchte diesen Ansatz vermeiden:
- (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 …
}
Bisher haben sich zwei Lösungen vorgestellt. Eine besteht darin, einen Selektor dynamisch zu konstruieren:
- (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
}
Bei diesem Ansatz müssen Sie den Epic- if()
Baum nicht bearbeiten , um einen neuen Typ zu unterstützen. Fügen Sie einfach die Methode hinzu, die die neue Klasse unterstützt. Dies ist ein guter Ansatz, wenn diese Tabellenansicht die einzige ist, die diese Objekte darstellen oder auf besondere Weise darstellen muss. Wenn die gleichen Objekte in unterschiedlichen Tabellen mit unterschiedlichen Datenquellen dargestellt werden sollen, schlägt dieser Ansatz fehl, da die Zellenerstellungsmethoden über die Datenquellen verteilt werden müssen. Sie können eine gemeinsame Superklasse definieren, die diese Methoden bereitstellt, oder dies tun:
@interface PhoneNumber (TableViewRepresentation)
- (UITableViewCell *)tableView: (UITableView *)tableView representationAsCellForRowAtIndexPath: (NSIndexPath *)indexPath;
@end
@interface Address (TableViewRepresentation)
//more of the same…
@end
Dann in Ihrer Datenquellenklasse:
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
id object = [self tableView: tableView representedObjectAtIndexPath: indexPath];
return [object tableView: tableView representationAsCellForRowAtIndexPath: indexPath];
}
Dies bedeutet, dass jede Datenquelle, die Telefonnummern, Adressen usw. anzeigen muss, nur das Objekt abfragen kann, das für eine Tabellensichtzelle dargestellt wird. Die Datenquelle selbst muss nichts mehr über das angezeigte Objekt wissen.
„Aber warten“ , höre ich eine hypothetische Gesprächspartner einwerfen, „nicht , dass Pause MVC? Sind nicht Sie Details in eine Modellklasse setzen?“
Nein, MVC wird nicht beschädigt. Sie können sich die Kategorien in diesem Fall als eine Implementierung von Decorator vorstellen . Dies PhoneNumber
ist eine Modellklasse, aber PhoneNumber(TableViewRepresentation)
eine Ansichtskategorie. Die Datenquelle (ein Controller-Objekt) vermittelt zwischen dem Modell und der Ansicht, sodass die MVC-Architektur weiterhin gültig ist.
Sie können diese Verwendung von Kategorien auch als Dekoration in Apples Frameworks sehen. NSAttributedString
ist eine Modellklasse, die Text und Attribute enthält. AppKit NSAttributedString(AppKitAdditions)
und UIKit bieten NSAttributedString(NSStringDrawing)
Dekoratorkategorien, die diesen Modellklassen das Zeichenverhalten hinzufügen.