iOS多种类型的cell处理方案

作者: 落叶随风_90e5 | 来源:发表于2019-07-24 18:13 被阅读0次

    在项目开发中UITableView和UICollectionView应该是最长用的控件了吧,而这两种控件的核心是cell的处理和展示。随着App的发展和需求的不断累加,页面是单一cell的情况越来越少,更多的是各种复杂cell的组合。常见的比如App的首页

    app首页示例图

    那么像这种页面我们是如何处理cell的呢?

    1.最常见的也是很多人会不经思考的,直接根据indexPath一一对应,写出下面的代码:

    - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{

        if(indexPath.section==0) {

        }

    }

    - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath{

        [tableViewdeselectRowAtIndexPath:indexPath animated:YES];

        if(indexPath.section==0) {

       }
    }

    虽然这种在开发阶段很容易,但是在后期的二次开发和维护上改一个地方tableview的delegate和datasource的方法都需要改,成本很高。而且cellForRowAtIndexPath的方法里面是清一色的if-else,然后是做了各种各样的事情,很容易造成代码的臃肿,动不动就是几十行或者几百行代码,不利于阅读和重用。

    这种方案的缺点有以下几点:

    1.一般情况下项目中不建议出现0、1等具体的数字,因为它除了表示位置之外,毫无其他意义。

    2.容易出错,在cell代理方法,高度代理方法,点击代理方法里面要保持一致,容易出错。

    3.不方便修改,如果要修改两个cell的顺序或者添加修改,要修改好几个地方,改动太大。

    2.根据model来对应cell,cell面向model开发

    前面提到了不因该出现indexPath等具体的位置数字,对于一个tableview,位置数字肯定是有的,我们要消除数字,那就得找到相应的数据来代替它。这里,主要的场景一般都是一个类型的数据(model)对应一种类型的cell,所以类型是固定的,所以我们用一个枚举来定义所有类型的cell

    typedefNS_ENUM(NSInteger, HomeCellType) {

     HomeCellTypeOne =0,

     HomeCellTypeTwo 

     HomeCellTypeThree, 

     HomeCellTypeFourl, 

     };

    然后在cellForRow方法,根据model类型加载对应的cell,例如:

       id model = self.viewModel.dataArray[indexPath.row];

    switch([self getHomeCellType] ){

      caseHomeCellTypeOne:

     HomeCellOne *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[HomeCellOne cellIdentifier] forIndexPath:indexPath];

       breke;

    caseHomeCellTypeOne:

     HomeCellTwo *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[HomeCellTwo cellIdentifier] forIndexPath:indexPath];

       breke;

       ....

    }

     - (HomeCellType)getHomeCellType:(id)model {

            HomeCellType type = HomeCellTypeOne;

            if([model isKindOfClass:[HomeCellTypeOneModel class]]) {

                type = HomeCellTypeOne;

            }else if([model isKindOfClass:[HomeCellTypeTwoModel class]]) {

                type = HomeCellTypeTwo;

            }else if(){

            }

          ...

    }

    这样看到了cellType或者model就知道如何去处理相应的cell了,清晰易理解。而且如果想复用、删除、添加、改动顺序,只需要改动数据源即可,其他不需要动,改动量很小。但是这样写的还不是很好,cell和datasource的cellForRowAtIndexPath耦合的还有点严重。那如果其他的地方只是用到了部分cell类型,我们还需要把上面的代码再copy一份?或者说我想让cell根据model去自动选择cell类型,而不是import各种cell。头文件,在cellForRowAtIndexPath方法里面判断,不依赖具体的cell呢?

    那么我的面向协议开发的设计模式就上场了。就是让model继承一个协议,该协议实现了cell的一些方法,例如cell的复用标示、cell的类型、cell的高度、cell的点击事件等。

    改进版

    1.定义协议接口

    @protocol ModelConfigProtocol

    @optional

    /**

    获取 cell 的复用标识

    @return 复用标识

    */

    - (nullableNSString*)cellReuseIdentifier;

    /**

    获取 cell 的类型

    @return cell 的类型

    */

    - (cellType)cellType;

    /**

    获取 cell 的高度

    @param indexPath indexPath

    @return 高度

    */

    - (CGFloat)cellHeightWithindexPath:(NSIndexPath*)indexPath;

    /**

    cell 点击

    @param indexPath indexPath

    @param other 其它对象

    */

    - (void)cellDidSelectRowAtIndexPath:(NSIndexPath*)indexPath other:(_Nullable id)other;

    2.然后实现实现该协议接口。定义一个抽象类的model遵守该协议实现协议

    @interface BaseModel : NSObject<ModelConfigProtocol>

    @end

    @implementation BaseModel

    - (cellType)cellType{

        return0;

    }

    - (NSString*)cellReuseIdentifier{

        return@"";

    }

    - (CGFloat)cellHeightWithindexPath:(NSIndexPath*)indexPath{

        return0.0;

    }

    - (void)cellDidSelectRowAtIndexPath:(NSIndexPath*)indexPath other:(_Nullable id)other{

        return;

    }

    @end

    3.具体的model继承自BaseModel,然后子类model具体实现ModelConfigProtocol的协议方法

    4.定义的一个抽象类的cell,开放赋值的接口

    @interfaceBaseCell : UITableViewCell

    @property (nonatomic,strong) id<ModelConfigProtocol> model;

    @end

    @implementation BaseCell

    - (void)setModel:(id)model{

    }

    @end

    5.具体的cell继承自BaseCell,然后子类cell具体实现setModel方法

    6.TableView代理里数据返回

    #pragma mark ---- UITableViewDelegate ----

    - (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath

    {

    id<ModelConfigProtocol> model =self.listArray[indexPath.row];

    return [model cellHeightWithindexPath:indexPath];

    }

    - (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath

    {

    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    id<ModelConfigProtocol> mdoel =self.viewModel.dataArray[indexPath.row];

    [model cellDidSelectRowAtIndexPath:indexPath other:nil];

    }

    #pragma mark ---- UITableViewDataSource ----

    - (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView;

    {

    return1;

    }

    - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section{

    return self.viewModel.dataArray.count;

    }

    - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath;

    {

    id<ModelConfigProtocol> model =self.viewModel.dataArray[indexPath.row];

        BaseCell *cell = [tableView dequeueReusableCellWithIdentifier:[model cellReuseIdentifier]];

        cell.cellConfig = model;

    returncell;

    }

    一般的一种类型的cell对应一种model,如果你想一种model对应多种cell,例如微信消息,有文本消息、图片消息、语音消息、红包消息、视频消息等。你可以在具体model的cellType再做一层判断。最厉害的地方在于可以和MVVM、适配器无缝对接,写一个BaseViewController实现这些,然后其他ViewController继承它,只需改变数据源,即可实现用最少的代码实现复杂的页面展示。

    相关文章

      网友评论

        本文标题:iOS多种类型的cell处理方案

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