Tôi tránh sử dụng UITableViewController
, vì nó đặt nhiều trách nhiệm vào một đối tượng. Do đó tôi tách UIViewController
lớp con khỏi nguồn dữ liệu và ủy nhiệm. Trách nhiệm của bộ điều khiển khung nhìn là chuẩn bị chế độ xem bảng, tạo nguồn dữ liệu với dữ liệu và nối các thứ đó lại với nhau. Thay đổi cách trình bày bảng xem có thể được thực hiện mà không thay đổi trình điều khiển chế độ xem và thực sự có thể sử dụng cùng một trình điều khiển chế độ xem cho nhiều nguồn dữ liệu theo mô hình này. Tương tự, thay đổi dòng công việc của ứng dụng có nghĩa là thay đổi bộ điều khiển xem mà không cần lo lắng về những gì xảy ra với bảng.
Tôi đã thử tách các giao thức UITableViewDataSource
và các UITableViewDelegate
giao thức thành các đối tượng khác nhau, nhưng cuối cùng thường bị phân tách sai vì hầu hết mọi phương thức trên đại biểu cần đào sâu vào nguồn dữ liệu (ví dụ: khi chọn, đại biểu cần biết đối tượng nào được đại diện bởi hàng được chọn). Vì vậy, tôi kết thúc với một đối tượng duy nhất là cả nguồn dữ liệu và đại biểu. Đối tượng này luôn cung cấp một phương thức -(id)tableView: (UITableView *)tableView representedObjectAtIndexPath: (NSIndexPath *)indexPath
mà cả hai khía cạnh nguồn dữ liệu và đại biểu cần biết những gì họ đang làm việc.
Đó là mối quan tâm "cấp 0" của tôi. Cấp 1 được tham gia nếu tôi phải đại diện cho các loại đối tượng khác nhau trong cùng một chế độ xem bảng. Ví dụ, hãy tưởng tượng rằng bạn phải viết ứng dụng Danh bạ cho một số liên lạc, bạn có thể có các hàng đại diện cho số điện thoại, các hàng khác đại diện cho địa chỉ, các hàng khác đại diện cho địa chỉ email, v.v. Tôi muốn tránh cách tiếp cận này:
- (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 …
}
Hai giải pháp đã trình bày cho đến nay. Một là tự động xây dựng bộ chọn:
- (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
}
Trong phương pháp này, bạn không cần chỉnh sửa if()
cây sử thi để hỗ trợ một loại mới - chỉ cần thêm phương thức hỗ trợ lớp mới. Đây là một cách tiếp cận tuyệt vời nếu chế độ xem bảng này là cách duy nhất cần thể hiện các đối tượng này hoặc cần trình bày chúng theo một cách đặc biệt. Nếu cùng một đối tượng sẽ được biểu diễn trong các bảng khác nhau với các nguồn dữ liệu khác nhau, cách tiếp cận này bị phá vỡ vì các phương thức tạo ô cần chia sẻ trên các nguồn dữ liệu mà bạn có thể định nghĩa một siêu lớp phổ biến cung cấp các phương thức này hoặc bạn có thể làm điều này:
@interface PhoneNumber (TableViewRepresentation)
- (UITableViewCell *)tableView: (UITableView *)tableView representationAsCellForRowAtIndexPath: (NSIndexPath *)indexPath;
@end
@interface Address (TableViewRepresentation)
//more of the same…
@end
Sau đó, trong lớp nguồn dữ liệu của bạn:
- (UITableViewCell *)tableView: (UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath {
id object = [self tableView: tableView representedObjectAtIndexPath: indexPath];
return [object tableView: tableView representationAsCellForRowAtIndexPath: indexPath];
}
Điều này có nghĩa là bất kỳ nguồn dữ liệu nào cần hiển thị số điện thoại, địa chỉ, v.v. chỉ có thể hỏi bất kỳ đối tượng nào được đại diện cho một ô xem bảng. Bản thân nguồn dữ liệu không còn cần phải biết bất cứ điều gì về đối tượng được hiển thị.
"Nhưng chờ đã," tôi nghe thấy một người đối thoại xen kẽ giả thuyết, "không phải nó phá vỡ MVC sao? Không phải bạn đã đưa chi tiết xem vào một lớp mô hình sao?"
Không, nó không phá vỡ MVC. Bạn có thể nghĩ về các danh mục trong trường hợp này như là một triển khai của Decorator ; vì vậy PhoneNumber
là một lớp mô hình nhưng PhoneNumber(TableViewRepresentation)
là một thể loại xem. Nguồn dữ liệu (một đối tượng điều khiển) làm trung gian giữa mô hình và khung nhìn, do đó kiến trúc MVC vẫn giữ.
Bạn cũng có thể thấy việc sử dụng các danh mục này làm trang trí trong các khung của Apple. NSAttributedString
là một lớp mô hình, giữ một số văn bản và thuộc tính. AppKit cung cấp NSAttributedString(AppKitAdditions)
và UIKit cung cấp NSAttributedString(NSStringDrawing)
, các danh mục trang trí có thêm hành vi vẽ cho các lớp mô hình này.