共计 4212 个字符,预计需要花费 11 分钟才能阅读完成。
TableView 是 iOS app 中最罕用的控件,许多代码间接或者间接的关联到 table view 工作中,包含提供数据、更新 tableView、管制 tableView 行为等等。上面会提供放弃 tableView 代码整洁和构造清晰的办法。
iOS 开发交换技术群:563513413,不论你是大牛还是小白都欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!
UITableViewController vs. UIViewController
TableViewController 的个性
table view controllers 能够读取 table view 的数据、设置 tabvleView 的编辑模式、反馈键盘告诉等等。同时 Table view controller 可能通过应用 UIRefreshControl 来反对“下拉刷新”。
Child View Controllers
tableViewController 也能够作为 child view controller 增加到其余的 viewController 中,而后 tableViewController 会持续治理 tableView,而 parentViewController 能治理其余咱们关怀的货色。
-(void)addDetailTableView
{
DetailViewController *detail = [DetailViewController new];
[detail setup];
detail.delegate = self;
[self addChildViewController:detail];
[detail setupView];
[self.view addSubview:detail.view];
[detail didMoveToParentViewController:self];
}
如果在应用以上代码时,须要建设 child View controller 和 parent view controller 之间的分割。比方,如果用户抉择了一个 tableView 里的 cell,parentViewController 须要晓得这件事以便可能响应点击工夫。所以最好的办法是 table view controller 定义一个协定,同时 parent view controller 实现这个协定。
@protocol DetailViewControllerDelegate
-(void)didSelectCell;
@end
@interface ParentViewController () <DetailViewControllerDelegate>
@end
@implementation ParentViewController
//….
-(void)didSelectCell
{
//do something…
}
@end
尽管这样会导致 view controller 之间的频繁交换,然而这样保障了代码的低耦合和复用性。
扩散代码
在解决 tableView 的时候,会有各种各样不同的,逾越 model 层、controller 层、view 层的工作。所以很有必要把这些不同的代码扩散开,避免 viewController 成为解决这些问题的“堆填区”。尽可能的独立这些代码,可能使代码的可读性更好,领有更好的可维护性与测试性。
打消 ModelObeject 和 Cell 之间的隔膜
在很多状况下,咱们须要提交咱们想要在 view 层展现的数据,同时咱们也行维持 view 层和 model 层的拆散,所以 tableView 中的 dateSource 经常做了超额的工作:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = [tableView dequeueReusableCellWithIdentifier:@”Cell”];
[cell setup];
NSString *text = self.title;
cell.label.text = text;
UIImage *photo = [UIImage imageWithName:text];
cell.photoView.image = photo;
}
这样 dataSorce 会变得很芜杂,应该将这些货色分到 cell 的 category 中。
@implementation Cell (ConfigText)
-(void)configCellWithTitle:(NSString *)title
{
self.label.text = title;
UIImage *photo = [UIImage imageWithName:title];
cell.photoView.image = photo;
return cell;
}
这样的话 dataSource 将会变得非常简略。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = [tableView dequeueReusableCellWithIdentifier:@”Cell”];
[cell configCellWithTitle:self.title];
return cell;
}
复用 cell
其实还能够更进一步,让同一个 cell 变得能够展现多种的 modelObject。首先须要在 cell 中定义一个协定,想要在这个 cell 中展现的 object 必须恪守这个协定。而后能够批改分类中 config method 来让 object 来恪守这个协定,这样 cell 就能适应不同的数据类型。
在 cell 中解决 cell 状态
如果想要对 tableView 的行为进行设置,如选中操作后扭转高光状态等,能够在 tableViewController 中应用委托办法:
-(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.label.shadowColor = [UIColor greenColor];
}
-(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
Cell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.label.shadowColor = nil;
}
然而当想要换出这些 cell 或者想要从新设计的时候,依然须要适应委托办法。cell 外面的 detail 的实现和委托办法中对 detail 的实现交错在一起,所以应该将这些逻辑移到 cell 外面:
@implementation Cell
//…
-(void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
{
[super setHighlighted:highlighted animated:animated];
if(highlighted)
{
self.label.shadowColor = [UIColor greenColor];
}
else
{
self.label.shadowColor = nil;
}
}
@end
一个委托须要晓得一个 view 的不同状态,但它不须要晓得怎么去批改 view 或者有哪些属性须要设置来使这个 view 转变状态,所有的逻辑应该又 view 来实现,而在内部只是仅仅提供一个 API。这样才是 view 层和 controller 层实现代码之间的无效拆散。
解决不同的 cell 类型
如果在一个 tableView 中有不同的 cell 类型,dataSource 将会变得收缩而难以操作,在上面的代码中,有两个不同的 cell 类型,一个负责展现图片和题目,另一个负责展现星标。为了拆散解决不同的 cell 的代码,dataSource 办法只是仅仅执行不同 cell 本人的设置办法。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL isStarRank = self.keys[(NSUInteger)indexPath.row];
UITableViewCell *cell;
if(isStarRank)
{
cell = [self setupStarCell];
}
else
{
cell = [self setupDefaultCell];
}
}
-(StarCell *)setupStarCell
{
//do something…
}
-(UITableViewCell *)setupDefaultCell
{
//do something…
}
编辑 TableView
TableView 提供了不便的编辑性能,可能删除和挪动 cell。这些事件中,tableView 的 dataSource 通过委托办法获取告诉,因而常常在这些委托办法中呈现对数据的批改,而批改数据很显著是 model 层的工作。model 应该提供删除、排序等的接口,这样就可能通过 dataSource 的办法来调用。从而 controller 表演了 view 和 model 之间的协调者,而不须要晓得 model 层的实现细节。同时这样 model 的逻辑会变得更容易测试,因为没有混淆 viewController 的工作。
总结
tableViewController 以及其余 controller 应该表演 model 和 view 的协调者和中介者,而不应该关怀属于 view 或者 model 层的工作。谨记这点,让委托和 dataSource 变得更小和只蕴含公式化的代码。
这不只是缩小 tableViewController 的体积和复杂度,同时也把域逻辑和界面逻辑放到相干的类中,把实现细节包裹在简略的 API 接口中,最终进步了代码的可读性和代码的协调能力。