前言
最近在開發(fā) iOS 時,發(fā)現(xiàn)有很多的業(yè)務(wù)展示界面都是使用了 tableView,而且大部分展示的界面都是沒有很復(fù)雜的業(yè)務(wù)交互和界面結(jié)構(gòu)。例如下面的2個界面
一般來說,正常的開發(fā)這上面的2個界面,會寫出一下的代碼
@interface ViewController1: UIViewController <UITableViewDataSource, UITableViewDelegate>
/* 省略內(nèi)容*/
@end
@implementation ViewController1
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.manager getDataSourceCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ViewController1CellConfig *config = [self.manager configInfoFromIndexPath: indexPath];
ViewController1Cell * cell = [tableView dequeueReusableCellWithIdentifier:@"ViewController1Cell" forIndexPath:indexPath ];
[cell configCell: ViewController1CellConfig];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return ViewControllerCellHeight;
}
@end
@interface ViewController2: UIViewController <UITableViewDataSource, UITableViewDelegate>
/* 省略內(nèi)容*/
@end
@implementation ViewController2
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.manager getDataSourceCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ViewController2CellConfig *config = [self.manager configInfoFromIndexPath: indexPath];
ViewController2Cell * cell = [tableView dequeueReusableCellWithIdentifier:@"ViewController2Cell" forIndexPath:indexPath ];
[cell configCell: ViewController2CellConfig];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return ViewControllerCellHeight;
}
@end
一對比,會發(fā)現(xiàn)這2個 ViewController 中的代碼非常類似,而在我們的業(yè)務(wù)項目中,遠遠不止2個類似的代碼。既然代碼都是如此的類似,那么,我們是否可以將這部分代碼抽離出來,抽象出一個公共的,復(fù)用的類呢。答案是肯定的。
觀察上面代碼可以發(fā)現(xiàn), 上述代碼,其實都是在重復(fù)一個流程, ViewController 從 manager 中獲取到數(shù)據(jù), 然后在將數(shù)據(jù)傳遞到 tableView 和 tableViewCell。 因此,只要我們確定了數(shù)據(jù)傳輸?shù)慕涌?,就可以將?shù)據(jù)傳輸這部分代碼抽象出來。
定義 Manager 到 ViewController 的接口
從上面代碼可知, 從 manager 中獲取數(shù)據(jù)的接口有 getDataSourceCount 和 configInfoFromIndexPath 所以,我們可以使用 Protocol 的特性, 定義一個接口文件EPCommonManagerProviderDelegagte,如下:
@protocol EPCommonManagerProviderDelegagte <NSObject>
- (id)cellConfigurationWithIndexPath:(NSIndexPath *)indexPath;
- (NSUInteger)numberOfRowsInSection:(NSInteger)section;
@end
這個協(xié)議一共定義了2個內(nèi)容,負責(zé)提供 tableView 行數(shù)的 numberOfRowsInSection 和 負責(zé)提供 tableViewCell 配置數(shù)據(jù)的 cellConfigurationWithIndexPath
定義 ViewController 到 tableView 和 Cell 的接口
由于 ViewController 到 tableView 的接口就是 dataSource 的協(xié)議內(nèi)容,這里不多討論。 下面主要定義了 ViewController 到 Cell 的接口EPCommonCellConfigDelegate
@protocol EPCommonCellConfigDelegate <NSObject>
- (void)cellConfig:(id)cellConfiguration;
@end
抽離出 ViewController 中 tableView 的 dataSource
@interface EPCommonTableViewProvider : NSObject <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, strong) id<EPCommonCellManagerProviderDelegagte> manager;
@property (nonatomic, strong) NSString *cellIdentifier;
@property (nonatomic, assign) CGFloat cellHeight;
@end
@implementation EPCommonTableViewProvider
- (instancetype)init {
if (self = [super init]) {
_cellHeight = 44;
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.manager && [self.manager respondsToSelector:@selector(numberOfRowsInSection:)]) {
return [self.manager numberOfRowsInSection:section];
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.cellIdentifier.length == 0) {
return [[UITableViewCell alloc] init];
}
id<EPCommonCellConfigDelegate> cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];
if (self.manager && [self.manager respondsToSelector:@selector(cellConfigurationWithIndexPath:)]) {
id cellConfiguration = [self.manager cellConfigurationWithIndexPath:indexPath];
if (cell && [cell respondsToSelector:@selector(cellConfig:)]) {
[cell cellConfig:cellConfiguration];
}
}
return (UITableViewCell *)cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return self.cellHeight;
}
@end
實際使用
- manager 實現(xiàn)
EPCommonManagerProviderDelegagte協(xié)議
@interface ViewController1Manager : NSObject <EPCommonManagerProviderDelegagte>
@end
@implementation ViewController1Manager
- ( *)cellConfigurationWithIndexPath:(NSIndexPath *)indexPath {
return [[ViewControllerCell1Config alloc] initWithModel:self.dataSource[indexPath.row] sortType:self.sortType];
}
- (NSUInteger)numberOfRowsInSection:(NSInteger)section {
return self.dataSource.count;
}
@end
- cell 實現(xiàn) EPCommonCellConfigDelegate 協(xié)議
@interface ViewController1Cell : UITableViewCell <EPCommonCellConfigDelegate>
@end
@implementation ViewController1Cell
- (void)cellConfig:(ViewControllerCell1Config *)config {
/* 配置 cell */
}
@end
- 將 EPCommonTableViewProvider 配置 給 tableView 的 dataSource
@interface ViewController1: UIViewController
/* 省略內(nèi)容*/
@property (nonatomic, strong) EPCommonTableViewProvider *provider;
@property (nonatomic, strong) ViewController1Manager *manager;
@property (nonatomic, strong) UITableView *tableView;
@end
@implementation ViewController1
- (EPCommonTableViewProvider *)provider {
if (_provider == nil) {
_provider = [[EPCommonTableViewProvider alloc] init];
_provider.manager = self.manager;
_provider.cellHeight = 50;
_provider.cellIdentifier = @"ViewController1Cell";
}
return _provider;
}
- (UITableView *)tableView {
if (_tableView == nil) {
_tableView = [[UITableView alloc] init];
_tableView.dataSource = self.provider;
[_tableView registerNib:[UINib nibWithNibName:@"ViewController1Cell" bundle:nil] forCellReuseIdentifier:@"ViewController1Cell"];
}
return _tableView;
}
@end
對于簡單的展示頁面,manager 和 cell 的代碼幾乎沒有變化,但是抽離出一個 EPCommonTableViewProvider 可以有效的減少 ViewController 中代碼行數(shù),并且也可以減少重復(fù)代碼的數(shù)量。