【iOS开发】UITableView和UICollectionView多种类型cell处理,更好地组织代码

问题点介绍

开发不仅仅是完成功能,还要写出认后来人可以很容易上手维护的代码。今天就记录一下列表数据,多种类型cell时,如何更好的组织代码。从实际需求场景出发,先看一下UI效果图。

刚开始做开发的人,碰到table view什么的,很容易写出下面这样的代码:

if (indexPath.section == 0) {

  if (indexPath.row == 0) {

  }
  else if (indexPath.row == 1) {

  }
}
else if (indexPath.section == 2) {

}

各种各样的魔法数字。刚写的那会还好,还知道是什么意思,能很快的找到要修改的地方。过了一两个月,再有需求修改的话,写的人自己都要读好久的代码,还容易出错。如果让别人接手,那就有点痛苦了。

举个栗子:我看过一个商品详情页面的代码,一个cellForRow方法里面有650行代码。cellForRow方法里面做了各种各样的事情,最主要的是清一色的上面这样的if else。

先从大的方面列几点建议:

  1. 目前一直用MVVM的模式开发,所以数据请求,加工处理应该放在ViewModel里面。
  2. cellForRow方法,应该只是做为Data跟View的一个接口处,不应该放各种设置代码,处理代码。相应的代码应该放到cell里面去处理。
  3. 不要用0,1这样的魔法数字。今天主要讲这点。

用魔法数字的缺点

  • 无意义,0、1这样的数字只能表示位置。没有其它的信息。
  • 容易出错,在cell代理方法,高度代理方法,点击代理方法里面要保持一致,容易出错。
  • 不方便修改,如果要修改两个cell的顺序,要修改好几个地方。

下面说一下解决方法,并不是什么高深的东西,有一定开发经验的人应该都懂。对于一个tableview,位置数字肯定是有的,我们要消除数字,那就得找到相应的数据来代替它。这里,主要的场景一般都是一个row对应一种类型的cell,所以类型是固定的,所以我们用一个枚举来定义所有类型的cell。

typedef NS_ENUM(NSInteger, HomeCellType) {
    HomeCellTypeTopFunction = 0,
    HomeCellTypeToutiao, 
    HomeCellTypeToday, 
    HomeCellTypeSeckill, 
    HomeCellTypeActivity,    
    HomeCellTypeSpecialTopic,    
};

上面是我从项目里直接复制出来的,请忽略名字(取名真是一个痛苦的事【抱头】)。列表的数据一般都是放在一个Array里面,还是以我上面的例子来说明,我有6种类型的model,有些model可能有多个,像上面枚举里面最后那个类型的model就可能有多个。从服务器拉回数据后,我就在vm里面解析好,全放到一个array里面了,就是列表的数据源。下面是我的cellForRow方法:

id model = self.viewModel.dataArray[indexPath.row];
        switch ([self getHomeCellType:model]) {

            case HomeCellTypeTopFunction: {

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

                cell.viewModel = model;
                return cell;

                break;

            }

            case HomeCellTypeToutiao: {

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

                cell.viewModel = model;
                return cell;

                break;

            }

                

            case HomeCellTypeToday: {

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

                cell.viewModel = model;
                return cell;

                break;
            }

            case HomeCellTypeActivity: {

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

                cell.viewModel = model;
                return cell;

                break;
            }

            case HomeCellTypeSeckill: {

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

                cell.viewModel = model;
                return cell;

                break;
            }

            case HomeCellTypeSpecialTopic: {

                IndexSpecialTopicCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[IndexSpecialTopicCell cellIdentifier] forIndexPath:indexPath];
                cell.viewModel = model;
                return cell;

                break;
            }

    default:

                break;
        }

这样的cellForRow方法是不是很简洁。里面的getHomeCellType方法是通过dataArray里面model的类型,拿到对应的cell类型。方法如下:

- (HomeCellType)getHomeCellType:(id)model {

    HomeCellType type = HomeCellTypeTopFunction;

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

        type = HomeCellTypeToutiao;
    }
    else if ([model isKindOfClass:[SeckillCellViewModel class]]) {

        type = HomeCellTypeSeckill;
    }
    else if ([model isKindOfClass:[IndexTodayCellViewModel class]]) {

        type = HomeCellTypeToday;
    }

    else if ([model isKindOfClass:[IndexActivityCellViewModel class]]) {

        type = HomeCellTypeActivity;
    }
    else if ([model isKindOfClass:[IndexSpecialTopicCellViewModel class]]) {

        type = HomeCellTypeSpecialTopic;
    }

    return type;
}

不是公共代码我们一般不加项目前缀,有点多余的感觉。

优点

  • 看到枚举的名字一般就知道这个cell对应到view上的哪个。清晰、易理解。
  • 要修改cell的顺序只要修改dataArray里面的顺序就可以。不用改动其它的代码。
  • 其它的代理方法也是这个写法,隐藏、添加cell、改动顺序什么的都不需要改动。
  • 易于维护,就算一个新人接手这样的代码,加上一定量的注释,可以很快的熟悉并上手修改。

一些其它的方式

  • cell的type也可以放到model里面去。
  • 如果有多个section的话,还可以定义一个section的枚举,再定义每个section对应的row的枚举。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java学习网

Java Web Response对象的27个方法及状态码

response表示HttpServletResponse对象,主要将JSP容器处理后的结果传回到客户端。 ? 网络配图 1、void addCookie(...

58570
来自专栏青玉伏案

窥探Swift之协议(Protocol)和委托代理(Delegate)回调的使用

  协议与委托代理回调在之前的博客中也是经常提到和用到的在《Objective-C中的委托(代理)模式》和《iOS开发之窥探UICollectionViewCo...

33880
来自专栏lzj_learn_note

阿里ARouter拦截器使用及源码解析(二)

关于ARouter基本跳转的用法以及源码解析在上篇文章阿里阿里ARouter使用及源码解析(一)已经有过分析,有不清楚的同学可以去看看。本篇文章主要是关于ARo...

36630
来自专栏LanceToBigData

Hadoop(五)搭建Hadoop客户端与Java访问HDFS集群

前言   上一篇详细介绍了HDFS集群,还有操作HDFS集群的一些命令,常用的命令: hdfs dfs -ls xxx hdfs dfs -mk...

74490
来自专栏Android知识点总结

3-AIV--使用ContentProvider获得所有图片路径

21720
来自专栏码神联盟

碎片化 | 第四阶段-56-ManyToOne和OneToMany映射关系-视频

http://v.qq.com/x/page/f0567zgfet1.html ManyToOne (多对1) 表结构: note表----userno...

34780
来自专栏mukekeheart的iOS之旅

Android基础总结(5)——数据存储,持久化技术

瞬时数据:指那些存储在内存当中,有可能会因为程序广播或其他原因导致内存被回收而丢失的数据。 数据持久化:指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机...

31570
来自专栏IT技术精选文摘

ZooKeeper 分布式锁实现

19720
来自专栏程序猿DD

死磕Java并发:J.U.C之阻塞队列:DelayQueue

DelayQueue是一个支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,如果队列里面没有元素到期,是不能从...

10210
来自专栏LinXunFeng的专栏

iOS - RxSwift 项目实战记录

23930

扫码关注云+社区

领取腾讯云代金券