案例一:微信,按发送和接收分为左右,图片、文本、语音、红包、文件等等;
案例二:微博,图文混合。
相比微博,那显然是微信的cell来得更复杂,那我们拿类微信的案例来做一次设计。
一、Cell个数
假设消息类型只有文本、图片、语音、红包、文件这5种,加上系统消息(时间分割)一共6种类型。那么我们按照发送和接收分开来计算就是6*2=12种,也就是说我们最多能分为12个Cell。那么问题来了,我们到底需要多少个Cell,先了解下TableView的运行机制。一个TableView假设是单一的Cell复用,手机屏幕上同时最多可见5个cell,那么实际创建的是大于5个Cell的,不然是来不及复用的。那么我们假设这种情况下系统创建了10个(足够用了),那么假设我们设计了12种不同的Cell,理论上来讲系统可能会创建12*10=120个那么多(当然不是那么精确的)......说了那么多,其实没有人会去计较到底有多少个,我只关心的是卡不卡。在现在iPhone的迭代速度来看,支撑120个cell也不是什么问题,即使是在iPhone4上。那么我们来讨论下另一个极端,就是最少我们可以设计1个Cell承接所有消息类型,我相信只要花点时间和死掉几亿个脑细胞去做适配和hidden = YES/NO一定能搞定,并且有一点是毋庸执意的,这样的方式性能是最好的,但是累死了猿们。
上面废话可以简化到一句话:Cell类型越少,性能越好,设计开发维护越难。Cell类型越多,性能越差,设计开发维护越容易。
在开发IM过程中,这两个我都用过,前者是第一版IM,我真的把这么多业务压缩到一个Cell里面去了,最后维护得差点想死,我能活着,还要感谢公司让我开发了第二版,所以我乘机尝试了后者,12个Cell,分的清清楚楚,维护极其容易,甚至在同一个消息业务下,左右不对称(比如群消息中显示接收人的头像,隐藏自己发送的头像)都只需要随意改一个Cell就可以,突然觉得开发简直是一种享受。我们总是很尽职地为一切设想,然后现在的硬件水平已经不鸟我们在这方面付出的代价了(当然不是说性能不考虑,单指Cell这个事情),经过测试,我发现即使是12个Cell,在>=iPhone4的设备上跑得都很欢快。
总结:尽量压榨硬件换取开发成本,不提前优化性能,不过度优化性能,一切以真机运行为准,按需优化。花很大精力和时间,换来1秒到0.9秒的提升,那是傻子。
二、Cell排版
这个相信在Masonry库的协助下,都不是问题。
三、Cell高度
这个是整个设计中最难的部分,理解了其实很简单。由于Cell高度是动态计算的,并且是在Cell的内容排版之前就需要调用到,所以在此无法避免需要提前传入内容来做计算,这是无法避免的事情。
我的经验是:
1、在Cell中写公共方法来计算返回高度:
这样的好处是可以复用Cell中采用Masonry来排版的函数和UI。也就是说高度的计算核心方法,依旧是采用Masonry来排一遍全部的UI+根据传入内容来动态得到整个需要的高度。当然,简单的布局可以直接用设定好的常量来计算并返回,比如语音、图片等。
+(CGFloat)getHeightWithModel:(Model *)model;
2、缓存计算好的高度:
仅有第一步还是不够,因为Cell会被不断复用,也就是在来回滚动的过程中,你曾经计算好的Cell高度在被服用掉之后再次使用的时候依旧会重新计算,虽然硬件不是太介意你这么干,但是这是付出小代价获得大提升的地方。做法就是Array中的Model里增加一个cellHeight的属性,用于保存计算过的Cell高度,这样就可以避免重复计算。
四、动态的图片高度
我曾经也遇到过这样的问题,就是使用SDWebImage类库异步加载图片,这是一个很头疼的问题,第一版IM没有经验,做了这样的设计:
if (ImageIsExist)—>getImageSize
else —>[SDWebImage Download] and [show PlaceholderImage]
通俗点讲就是,在Cell高度计算时候判断本地缓存中是否已存在,存在则拿出来计算size并渲染;不存在就去下载,由于图片是异步下载+Cell不断被复用,所以Block回调并不能保证返回的和现在请求的是不是同一张图片,所以需要model来做防御。而且列表会跳动,因为默认图片是方的,接收到的图片尺寸不一,体验是很糟糕的。
后来第二版中参考微信做了优化,在拿到图片消息的时候,附带了图片的地址和图片缩略图的长宽,这样一来,我可以正常的使用这个长宽来渲染UIImageView,而图片异步下载完毕后仅仅需要更新下即可,免去计算之苦,所以说,设计的重要性对于开发来讲是多么重要。做开发到一定程度,大家水平都差不多,拼得就是设计了,也就是架构。
总结:
以上就是主题的思路,主要能理解设计,我相信任何场景和设计都难不倒开发。