寫在前面
在之前的項目中好多處用到了tableView,然而之前不懂得將代理方法實現分離,所以每在一處用到tableView就要在controller中寫一遍UITableViewDataSource和UITableViewDelegate,實現tableView的基本功能中用到的tableViewDelegate的方法還算不太多,但是說到UITableViewDataSource,里面就那些固定的用法,每次都要寫一遍,大大增加了代碼的冗余度,雖然不吝嗇體力多寫幾行代碼,但是給人感覺不太好,接下來就來說一下怎么樣將tableViewDataSource這個磨人的小妖精從controller中分離出來。
創建一個基于NSObject的ArrayDataSource類
這個類就是我們將UITableViewDataSource
分離出來所封裝的類。繼承協議@interface ArrayDataSource : NSObject<UITableViewDataSource>
。
OK,開始我們的分離之路:
1、
首先在.h文件中,我們定義一個block:typedef void (^TableViewCellConfigureBlock)(id cell, id items);
block中需要的兩個參數:第一個參數是cell,第二個參數是數據(這個數據可以是model或者字典)。
2、
在.h文件中寫兩個作為外部調用的接口:
第一個函數:
@param anItems 傳入的盛裝數據的數組
@param aCellIdentifier cell的標示符
@param aConfigureCellBlock 回調的block
- (id)initWithItems:(NSArray *)anItems
cellIdentifier:(NSString *)aCellIdentifier
configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock;
第二個函數:
將傳入數組中的數據按照cell的indexPath使用(這個我不知道怎么表述清楚,這個方法就是之前在- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
這個函數中將數組中的數據根據cell的索引值給cell中的控件賦值,不知道這樣說能不能引起大家的共鳴……_)
- (id)itemAtIndexPath:(NSIndexPath *)indexPath;
3、
在.m中,我們聲明三個全局變量:
@interface ArrayDataSource ()
@property(nonatomic, strong) NSArray* items;/**< array */
@property(nonatomic, copy) NSString* cellIdentifier;/**< cellIdentifier */
@property(nonatomic, copy) TableViewCellConfigureBlock configureCellBlock;/**< block */
@end
實現.h中的方法:
- (instancetype)init {
return nil;
}
調用初始化方法時將外部數據賦值給內部參數
- (id)initWithItems:(NSArray *)anItems cellIdentifier:(NSString *)aCellIdentifier configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock {
self = [super init];
if (self) {
self.items = anItems;
self.cellIdentifier = aCellIdentifier;
self.configureCellBlock = aConfigureCellBlock;
}
return self;
}
根據cell的索引值,將傳入的數據分離
- (id)itemAtIndexPath:(NSIndexPath *)indexPath {
return self.items[(NSUInteger) indexPath.row];
}
#pragma mark - UITableViewDataSource
實現UITableViewDataSource的方法。。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier];
id item = [self itemAtIndexPath:indexPath];
//在這兒將block傳出。
self.configureCellBlock(cell, item);
return cell;
}
在Controller中使用
OK,以上的這個類就是我們分離出來的DataSource的類了
在controller中我使用的假數據,將數據放在一個數組arr中,這里我用到了懶加載,在這里我遇到一個困惑就是為什么在if (!_arr)
條件判斷中不能使用self.arr,而只能用_arr,但是在if (!_arr) { self.arr = @[@{@"name":@"實現tableViewController的瘦身"}}]; }
中卻可以使用self.arr. 我查了一下資料找到答案,_arr是直接值訪問,而self.arr是屬性訪問,就是通過get/set方法來讀取這個值,xcode會默認將兩個值通過syncthesize關鍵字進行同步,- (NSArray *)arr
這個方法就是self.arr的get方法,也就是說每次你調用self.arr的時候都會進入這個方法,如果在這個方法里用了下面這個語句if (!self.arr );
邏輯上是行不通的,因為在這里調用self.arr他會再一次進入這個方法,理論上就會死循環,而_arr是直接值訪問的,他不會調用get/set方法,所以就不會有這個問題.
那在if (!_arr) { self.arr = @[@{@"name":@"實現tableViewController的瘦身"}}]; }
- (NSArray *)arr {
if (!_arr) {
self.arr = @[@{@"name":@"實現tableViewController的瘦身"}}];
}
return _arr;
}
我們在controller中使用tableView的時候需要這樣做:
- (void)createTableView {
self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
[self.tableView registerClass:[LGJTableViewCell class] forCellReuseIdentifier:@"cell"];
self.tableView.rowHeight = 70;
[self.view addSubview:self.tableView];
//這里我將block中的第二個參數(id items)傳入了一個字典。因為我的數據是存放在數組中的字典中的。
TableViewCellConfigureBlock configureCell =^(LGJTableViewCell *cell, NSDictionary *dic) {
//在這里操作cell中的控件,或者給cell中的控件賦值
[cell configData:dic];
};
self.dateSource = [[ArrayDataSource alloc] initWithItems:self.arr cellIdentifier:@"cell" configureCellBlock:configureCell];
self.tableView.dataSource = self.dateSource;
}
總結
經過抽取,我們將dataSource從controller中分離出來,這樣不用每次使用tableView的時候我們都要重復寫一遍dataSource代理了,同時也簡化了代碼結構。希望小伙伴們如果覺得有不妥的地方幫我指出來,我們一同進步。end