本篇文章主要的目的是分享一種UITableView模塊化開(kāi)發(fā)的思路。
在開(kāi)發(fā)過(guò)程中,常常會(huì)遇到很復(fù)雜的tableview的界面,往往每一個(gè)的section都是完全不一樣的。比如這樣:

這個(gè)界面中,每一個(gè)section都是不同的。當(dāng)需要多人去開(kāi)發(fā)這樣的界面,每個(gè)人負(fù)責(zé)一到兩個(gè)section,如何去管理大家的代碼,讓代碼互不沖突呢?
解決方法就是分模塊化開(kāi)發(fā)。大致的思路是這樣的:控制器中不寫(xiě)任何和各個(gè)section相關(guān)的代碼,只需創(chuàng)建UITableView并編寫(xiě)其相關(guān)的數(shù)據(jù)源方法。這里的每一個(gè)section對(duì)應(yīng)一個(gè)繼承自NSObject的control類(lèi)。在控制器中,添加一個(gè)個(gè)control到數(shù)組controls中??蓪?code>control作為一個(gè)控制器,所有的數(shù)據(jù)獲取,邏輯處理均可在其中完成。
說(shuō)到這里,你如果看過(guò)自造輪子:YQCommonCell 簡(jiǎn)化表單視圖開(kāi)發(fā),應(yīng)該會(huì)發(fā)現(xiàn)兩者在思路上是類(lèi)似的,將section對(duì)應(yīng)模型抽離出,相關(guān)section的屬性均在模型中編寫(xiě)。
但是,目前存在兩個(gè)問(wèn)題:
1.如何將每一個(gè)section都正確的顯示在界面上?;蛘哒f(shuō),section界面設(shè)置的入口改在哪?
2.control想要刷新tableview的時(shí)候,該如何去做。
1.如何將每一個(gè)section都正確的顯示在界面上?;蛘哒f(shuō),section界面設(shè)置的入口改在哪?
在初學(xué)時(shí),遇到這種界面,第一反應(yīng)就是創(chuàng)建自定義視圖cell,在cellForRow中設(shè)置對(duì)應(yīng)的cell。這樣做的代價(jià)是所有的數(shù)據(jù)請(qǐng)求等都在控制器中完成,而現(xiàn)在要由control來(lái)完成。在control中設(shè)置一個(gè)全局屬性view,然后再去cellForRow設(shè)置?不是的。
其實(shí)方法很簡(jiǎn)單,在tableView數(shù)據(jù)源的方法cellForRow中,讓每一個(gè)control去實(shí)現(xiàn)cellForRow方法,也就是說(shuō),在control中,自行設(shè)置其對(duì)應(yīng)的section的cell。其他的section屬性也是類(lèi)似的。
那么,可寫(xiě)個(gè)協(xié)議,我暫時(shí)命名為YQModuleResolveControlProtocol。在該協(xié)議中,設(shè)置必須實(shí)現(xiàn)的方法:
@required
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row;
- (CGFloat)heightForRow:(NSInteger)row;
- (NSInteger)numberOfRows;
還有可選擇實(shí)現(xiàn)的方法:
@optional
- (CGFloat)heightForHeader;
- (UIView *)viewForHeader;
- (CGFloat)heightForFooter;
- (UIView *)viewForFooter;
- (void)tableView:(UITableView *)tableView cellSelectIndexPath:(NSIndexPath *)indexPath;
只需讓每一個(gè)control遵循YQModuleResolveControlProtocol,實(shí)現(xiàn)必須實(shí)現(xiàn)的方法。
在控制器中,以cellForRow為例:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section >= self.controls.count) {
return [[UITableViewCell alloc] init];
}
id <YQModuleResolveControlProtocol>control = self.controls[indexPath.section];
if ([control respondsToSelector:@selector(tableView:cellForRow:)]) {
return [control tableView:tableView cellForRow:indexPath.row];
}
return [[UITableViewCell alloc] init];
}
這樣,很完美的將代碼抽離到一個(gè)個(gè)模塊中。
2.control想要刷新tableview的時(shí)候,該如何去做。
這個(gè)問(wèn)題的本質(zhì)就是數(shù)據(jù)傳遞或事件傳遞,很簡(jiǎn)單,用代理即可。
新的協(xié)議:
@protocol YQModuleResolveControlDelegate <NSObject>
@optional
- (void)reloadControl:(id)control row:(NSInteger)row;
- (void)reloadOwnControl:(id)control;
- (void)reloadAllTableView:(UITableViewRowAnimation)animation;
@end
讓控制器遵循該協(xié)議,并實(shí)現(xiàn)(以刷新某一行為例):
#pragma mark - YQModuleResolveControlDelegate
- (void)reloadControl:(id)control row:(NSInteger)row
{
if ([self.controls containsObject:control]) {
NSInteger section = [self.controls indexOfObject:control];
NSIndexPath *indexpath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tv reloadRowsAtIndexPaths:@[indexpath] withRowAnimation:UITableViewRowAnimationFade];
}
}
這樣,就能做到control隨時(shí)主動(dòng)刷新tableView。
以上,就能做到本文最開(kāi)頭所寫(xiě)的要求,將每一個(gè)section對(duì)應(yīng)的所有代碼抽離至control。具體開(kāi)發(fā)時(shí),讓每一位開(kāi)發(fā)人員自行創(chuàng)建control即可。而ViewController將會(huì)一身輕松且不需要時(shí)常修改代碼。
順便補(bǔ)充一點(diǎn),如果想讓ViewController編寫(xiě)完后就再也不修改任何代碼,可將控制器controls添加control這一部分也抽離到分類(lèi)中,每一位開(kāi)發(fā)人員自行去分類(lèi)中添加自己的control即可。