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)
.