UITableViewController
単一のオブジェクトに多くの責任を課すので、使用は避けます。したがって、UIViewController
サブクラスをデータソースとデリゲートから分離します。View Controllerの役割は、Table Viewを準備し、データを含むデータソースを作成し、それらを一緒にフックすることです。TableViewの表現方法の変更は、View Controllerを変更せずに実行できます。実際、同じView Controllerを、このパターンに従う複数のデータソースに使用できます。同様に、アプリのワークフローを変更すると、テーブルに何が起こるか心配することなくView Controllerを変更できます。
プロトコルUITableViewDataSource
とUITableViewDelegate
プロトコルを異なるオブジェクトに分離しようとしましたが、デリゲートのほとんどすべてのメソッドがデータソースを掘り下げる必要があるため、通常は誤った分割になります(たとえば、選択時に、デリゲートは選択された行)。したがって、データソースとデリゲートの両方である単一のオブジェクトになります。このオブジェクトは常に-(id)tableView: (UITableView *)tableView representedObjectAtIndexPath: (NSIndexPath *)indexPath
、データソースとデリゲートの両方の側面が何に取り組んでいるかを知る必要があるメソッドを提供します。
それが私の「レベル0」の関心事の分離です。同じテーブルビューで異なる種類のオブジェクトを表現する必要がある場合、レベル1が使用されます。例として、連絡先アプリを作成する必要があることを想像してください。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 …
}
これまでに2つのソリューションが提示されています。1つは、セレクターを動的に構築することです。
- (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)
がこれらのモデルクラスに描画動作を追加するデコレータカテゴリを提供します。