iOS UITableView的简单封装

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所有的代理方法,也都不用在写在控制器中,影响视线了。

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 7,926评论 3 38
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 4,352评论 1 23
  • UITableViewCell控件空间构造 cell的子控件是contentView,contentView的子控...
    CoderZXS阅读 359评论 0 1