【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的枚举。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容