美文网首页ios
iOS UITableView的简单封装

iOS UITableView的简单封装

作者: WXGD | 来源:发表于2016-08-16 15:53 被阅读1339次

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

    相关文章

      网友评论

      • 雪_晟:有 没有 demo啊 大神
        799a33b5be66:tableViewCellClass谁来实现
        雪_晟:@WXGD 好的 太感谢了
        WXGD:推荐你关注一下bestswifter,他有一篇对tableview的封装,有dome还有视频,封装的要比我这好的多。

      本文标题:iOS UITableView的简单封装

      本文链接:https://www.haomeiwen.com/subject/abppsttx.html