iOS实现UITableViewDataSource与Contr

作者: 劉光軍_MVP | 来源:发表于2016-04-08 16:29 被阅读2839次
    写在前面

    在之前的项目中好多处用到了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

    项目地址:https://github.com/irembeu/DataSourceTableViewTest.git

    相关文章

      网友评论

      • bill666500:为什么我按照博主写的cellForRowAtIndexPath这个方法不调用 我注册Cell了
        劉光軍_MVP:@bill666500 :smile::smile::smile:666 解决了就好
        bill666500:@劉光軍_Shine 知道原因了 你用了strong 声明属性dataSource 我没有声明 直接创建 就很可能释放了
        劉光軍_MVP:@bill666500 你运行一下我的demo看看行不行
      • FlyHo:把数据传到DataSource里面直接config cell不行吗?为什么还要回调到controller里面config cell?请指教
        劉光軍_MVP:@FlyHo 根据实际业务来呗。如果害怕一个过于臃肿 可以分类写多个datasource 供我们使用。不是只局限于那一个
        FlyHo:@劉光軍_Shine 回得好快啊!谢谢!还有如果cell有很多,datasource也会很臃肿,根据cellID区分,if-else也会很多,这怎么破?
        劉光軍_MVP:@FlyHo datasource是一个公用的 cofigcell属于自定义 如果你把cell的配置都放在了分离出来的datasource里面 那分离出来也就没有什么意义了
      • 薛定谔的熊:。。。中使用self.arr,虽然他也会进入get方法,但是他已经不等于nil了.会直接返回他本身,所以没有问题. 这句话是错的好吗,请自己先研究清楚了再发博客,误人子弟。
        劉光軍_MVP:@薛定谔的熊 是 你说的对
        薛定谔的熊:@劉光軍_Shine self.arr = @[@{@"name":@"实现tableViewController的瘦身"}}]; 不是进入get方法,是set方法。 记住等号左边的self.是赋值(set),等号右边的self.是取值(get)。兄弟,过了一年水平没看涨呀
        劉光軍_MVP:@薛定谔的熊 我回去研究清楚了再修改 以后再也不误人子弟了 谢谢指教
      • 小的小碰撞:受教了,有空试试
      • pinksnow:Objc , 那么问题来了 ,有多种Cell样式 怎么分离
        16c2832b2c28:将自定义的cell 以参数的形式传进去,就可以自定义多种cell样式
        self.dataSource = [[ArrayDatasource alloc] initWithItems:self.dataArray cellIdentifier:identifier Class:self.cell configBlock:configBlock];
        劉光軍_MVP:根据cell的标识符来区别啊
      • xxttw:还可以阿
        劉光軍_MVP:@Unc1eWang :smile::smile:
      • 11eddd582230: self.tableView.dataSource = self.dateSource;这个好像会造成循环引用吧,你有在dealloc里面打印,看页面会不会销毁吗。我就遇到了这个问题
      • Caiflower:如果是分组的呢!
      • fc18f69e6ff0:为什么我按步骤来操作会出现崩溃
        劉光軍_MVP:@闭上眼睛 可以在model里面优化。在controller里面 调用datasource的时候 那个block 直接往里面传model就行
        fc18f69e6ff0:@劉光軍_ 恩,后来发现了,还有一个问题,这个item得数组是要网络下载的,不可能每次下载都去初始化一遍,arrdatasource这个怎么优化呢
        劉光軍_MVP:@fc18f69e6ff0 你是不是没有在tableview那里注册cell。要先注册cell
      • 啃手高手:还可以这样!学习了~
        劉光軍_MVP:@Goyakod 就跟咱们自定义cell套路差不多,

      本文标题:iOS实现UITableViewDataSource与Contr

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