【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。
先从大的方面列几点建议:
- 目前一直用MVVM的模式开发,所以数据请求,加工处理应该放在ViewModel里面。
- cellForRow方法,应该只是做为Data跟View的一个接口处,不应该放各种设置代码,处理代码。相应的代码应该放到cell里面去处理。
- 不要用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的枚举。
- PHP基础——PHP数组
- 使用shell抽取html数据之二(r2笔记75天)
- Python爬取链家网数据:新房楼盘价格分析
- 【编程基础】Java里面如何对字符串排序?
- 计算广告——广告定向实践
- 通过shell抓取html数据(r2笔记74天)
- 通过shell脚本分析足彩(r2笔记74天)
- 通过shell脚本得到数据字典的信息 (r2笔记72天)
- 机器学习算法实践——K-Means算法与图像分割
- 利用 Python、SciKit 和文本分类来构建客户行为描述模型
- 使用Python爬取社交网络数据分析
- PHP爬虫源码:百万级别知乎用户数据爬取与分析
- 使用Python抓取欧洲足球联赛数据
- python爬取百度新闻:分析共享单车火爆背后有哪些规则?
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- PHP实现小程序批量通知推送
- Thinkphp5.0 框架Model模型简单用法分析
- php设计模式之单例模式用法经典示例分析
- PHP实现统计代码行数小工具
- redis+php实现微博(三)微博列表功能详解
- php设计模式之工厂模式用法经典实例分析
- laravel 关联关系遍历数组的例子
- PHP开启目录引索+fancyindex漂亮目录浏览带搜索功能
- 解决Laravel 使用insert插入数据,字段created_at为0000的问题
- 关于php unset对json_encode的影响详解
- php进行md5加密简单实例方法
- Laravel timestamps 设置为unix时间戳的方法
- php实现每日签到功能
- php+redis实现消息队列功能示例
- php使用curl模拟浏览器表单上传文件或者图片的办法