TableView 是iOS app 中最常用的控件,許多代碼直接或者間接的關(guān)聯(lián)到table view任務(wù)中,包括提供數(shù)據(jù)趁耗、更新tableView、控制tableView行為等等疆虚。下面會提供保持tableView代碼整潔和結(jié)構(gòu)清晰的方法苛败。
UITableViewController vs. UIViewController
TableViewController的特性
table view controllers可以讀取table view的數(shù)據(jù)满葛、設(shè)置tabvleView的編輯模式、反應(yīng)鍵盤通知等等罢屈。同時Table view controller能夠通過使用UIRefreshControl來支持“下拉刷新”嘀韧。
Child View Controllers
tableViewController也可以作為child view controller添加到其他的viewController中,然后tableViewController會繼續(xù)管理tableView缠捌,而parentViewController能管理其他我們關(guān)心的東西锄贷。
-(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之間的聯(lián)系曼月。比如谊却,如果用戶選擇了一個tableView里的cell,parentViewController需要知道這件事以便能夠響應(yīng)點(diǎn)擊時間十嘿。所以最好的方法是table view controller定義一個協(xié)議因惭,同時parent view controller實(shí)現(xiàn)這個協(xié)議。
@protocol DetailViewControllerDelegate
-(void)didSelectCell;
@end
@interface ParentViewController () <DetailViewControllerDelegate>
@end
@implementation ParentViewController
//....
-(void)didSelectCell
{
//do something...
}
@end
雖然這樣會導(dǎo)致view controller之間的頻繁交流绩衷,但是這樣保證了代碼的低耦合和復(fù)用性蹦魔。
分散代碼
在處理tableView的時候,會有各種各樣不同的咳燕,跨越model層勿决、controller層、view層的任務(wù)招盲。所以很有必要把這些不同的代碼分散開低缩,防止viewController成為處理這些問題的“堆填區(qū)”。盡可能的獨(dú)立這些代碼曹货,能夠使代碼的可讀性更好咆繁,擁有更好的可維護(hù)性與測試性。
消除ModelObeject和Cell之間的隔閡
在很多情況下顶籽,我們需要提交我們想要在view層展示的數(shù)據(jù)玩般,同時我們也行維持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會變得很雜亂礼饱,應(yīng)該將這些東西分到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;
}
復(fù)用cell
其實(shí)還可以更進(jìn)一步镊绪,讓同一個cell變得可以展示多種的modelObject匀伏。首先需要在cell中定義一個協(xié)議,想要在這個cell中展示的object必須遵守這個協(xié)議蝴韭。然后可以修改分類中config method來讓object來遵守這個協(xié)議够颠,這樣cell就能適應(yīng)不同的數(shù)據(jù)類型。
在cell中處理cell狀態(tài)
如果想要對tableView的行為進(jìn)行設(shè)置榄鉴,如選中操作后改變高光狀態(tài)等摧找,可以在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;
}
然而當(dāng)想要換出這些cell或者想要重新設(shè)計(jì)的時候核行,仍然需要適應(yīng)委托方法。cell里面的detail的實(shí)現(xiàn)和委托方法中對detail的實(shí)現(xiàn)交織在一起蹬耘,所以應(yīng)該將這些邏輯移到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的不同狀態(tài)芝雪,但它不需要知道怎么去修改view或者有哪些屬性需要設(shè)置來使這個view轉(zhuǎn)變狀態(tài),所有的邏輯應(yīng)該又view來完成综苔,而在外部只是僅僅提供一個API惩系。這樣才是view層和controller層實(shí)現(xiàn)代碼之間的有效分離。
處理不同的cell類型
如果在一個tableView中有不同的cell類型如筛,dataSource將會變得膨脹而難以操作堡牡,在下面的代碼中,有兩個不同的cell類型杨刨,一個負(fù)責(zé)展示圖片和標(biāo)題晤柄,另一個負(fù)責(zé)展示星標(biāo)。為了分離處理不同的cell的代碼妖胀,dataSource方法只是僅僅執(zhí)行不同cell自己的設(shè)置方法芥颈。
-(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通過委托方法獲取通知,因此經(jīng)常在這些委托方法中出現(xiàn)對數(shù)據(jù)的修改涂臣,而修改數(shù)據(jù)很明顯是model層的任務(wù)盾计。model應(yīng)該提供刪除、排序等的接口赁遗,這樣就能夠通過dataSource的方法來調(diào)用署辉。從而controller扮演了view和model之間的協(xié)調(diào)者,而不需要知道m(xù)odel層的實(shí)現(xiàn)細(xì)節(jié)岩四。同時這樣model的邏輯會變得更容易測試哭尝,因?yàn)闆]有混雜viewController的任務(wù)。
總結(jié)
tableViewController以及其他controller應(yīng)該扮演model和view的協(xié)調(diào)者和中介者炫乓,而不應(yīng)該關(guān)心屬于view或者model層的任務(wù)。謹(jǐn)記這點(diǎn)献丑,讓委托和dataSource變得更小和只包含公式化的代碼末捣。
這不只是減少tableViewController的體積和復(fù)雜度,同時也把域邏輯和界面邏輯放到相關(guān)的類中创橄,把實(shí)現(xiàn)細(xì)節(jié)包裹在簡單的API接口中箩做,最終提高了代碼的可讀性和代碼的協(xié)調(diào)能力。
以下文章可以做一個學(xué)習(xí)參考: