UITableView是iOS中最常用的控件之一,几乎所有的APP都不可避免的要用到这个控件。UITableView有两个代理需要遵守,分别是dataSource和delegate。其中dataSource有两个必须要实现的方法。我把dome上传到我的git@osc账号(table )上了,感兴趣的朋友可以下载看看。水平有限,如果有错误或者不合理的地方,非常希望听取你的意见。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
至于这两个方法是干嘛用的,我想就不需要多说了吧!
但是这两个方法明明是数据方法,却要放在控制器中实现,这不是不太符合MVC的思想吗!!而且每使用一次tableview,无论是复杂的tableview还是简单的tableview,都需要都最少要写这两个方法,作为一个懒惰的程序员这实在是太麻烦了。
所以封装的第一步就是将数据源方法独立出来。
#import "GJBaseDataSource.h"
@implementation GJBaseDataSource
#pragma mark - UITableViewDataSource
/** cell数 */
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
/** cell样式 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = @"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:className];
}
return cell;
}
@end
好了,大功告成!!!!是不是感觉我有些敷衍了事,其实对于UITableView的封装就是这么简单。只不过我们封装是为了提高代码的复用,而不单单是为了减少controller中的代码量。那么首先要解决的就是UITableViewCell的问题。
在实际开发中,UITableViewCell的功能实在是太单一了,很多时候都需要我们去自定义UITableViewCell的样式。所以我们要解决的就是怎么样才能方便指定cell的样式。其实也非常简单,无非就是写一个父类方法,让子类去实现就好了。
// 子类实现,用来去定cell类型
- (Class)tableViewCellClass {
// 这里设置了一个默认类型
return [UITableViewCell class];
}
然后只要按下面的方式指定cell类型
/** cell样式 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Class class = [self tableViewCellClass];
NSString *className = [NSString stringWithUTF8String:class_getName(class)];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:className];
if (!cell) {
cell = [[class alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:className];
}
return cell;
}
好了,对于数据源的封装到此就可以告一段落了。
接下来让我们想想delegate的代理方法。在tableview的delegate里有这样一个方法:
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath;
作用是用来指定cell的高度的。
当每次做到tableviewcell根据内容动态改变高度的时候,我都无比的羡慕安卓的做法。因为iOS实在是太麻烦了。
既然他是为了定义cell的高度,哪为什么我不能把他放在tableviewcell里面呢!!!说干就干,首先我需要自定义一个tableview,让他来实现delegate的方法,从而实现delegate方法和controller的分离。
在这里tableview里面实现delegate方法:
#import "GJBaseTabelView.h"
#import "GJBaseTableViewCell.h"
@implementation GJBaseTabelView
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style { self = [super initWithFrame:frame style:style];
if (self) {
self.delegate = self;
}
return self;
}
#pragma mark - UITableViewDelegate
/** cell高度 */
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return 100;
}
}
@end
然后,还需要自定义一个tableviewcell因为我需要在他的内部计算cell的高度啊!!
就像这样:
#import "GJBaseTableViewCell.h"
@implementation GJBaseTableViewCell
/** 每一个cell的高度 */
+ (CGFloat)tableView:(UITableView*)tableView rowHeightAtIndexPath:(NSIndexPath *)indexPath {
return 44;
}
@end
当我需要自定义tableviewcell的时候,我就需要继承与GJBaseTableViewCell这个基础cell,然后在里面重写rowHeightAtIndexPath方法,然后在heightForRowAtIndexPath中调用这个方法就好了。
/** cell高度 */
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return [GJBaseTableViewCell tableView:tableView rowHeightAtIndexPath:indexPath];
}
此时好像可以了,但是测试之后你会发现,cell的高度并没有改变啊!!!注意问题出在
return [GJBaseTableViewCell tableView:tableView rowHeightAtIndexPath:indexPath];
你调用rowHeightAtIndexPath方法时,使用的是GJBaseTableViewCell这个类名,也就是说你调用的是GJBaseTableViewCell的rowHeightAtIndexPath这个方法,而不是GJBaseTableViewCell
的子类重写的rowHeightAtIndexPath方法。要解决这个问题,就必须用你自定义的cell来调用这个方法。
还记得刚刚在数据源中那个获取自定义cell类的方法吗!没错就是他。
首先需要将tableViewCellClass方法写为一个代理方法
@protocol GJBaseDataSourceDelegate@optional
/** 设置cell样式 */
// 设置成代理,主要是为了在GJBaseTabelView中可以调用
- (Class)tableViewCellClass;
@end
这样才能在tableview类中方便的调用他。
我们刚过一只忽略了一个问题就是tableview的datasource,虽说在数据源类里面写了他的代理方法,可是这和tableview有关系吗?将它们联系起来,只需要
self.dataSource = [[GJBaseDataSource alloc] init];
就是将数据源代理设置成我们对数据源对象。然后因为tableViewCellClass是DataSource的代理方法,那么这个代理方法当然也可以用了。
/** cell高度 */
- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
id<GJBaseDataSourceDelegate>dataSource = (id<GJBaseDataSourceDelegate>)tableView.dataSource;
Class cls = [dataSource tableViewCellClass];
return [cls tableView:tableView rowHeightAtIndexPath:indexPath];
}
这样我们刚刚那个cell高度的问题也就同样结局了。
来试一下吧!此时在控制器中,应该只需要创建一个继承性于GJBaseTabelView的tableview就可以实现最简单的tableview了,而tableview所有的代理方法,也都不用在写在控制器中,影响视线了。