处理不等高TableViewCell

TableViewCell

今天来和大家一起聊聊处理不等高TableViewCell的那些小花招

课题一:如何计算Cell高度

方案一:直接法(面向对象)

想知道妹纸爱你有多深?直接去问妹纸本人吧!

嗯!Cell也是一样的,想知道cell到底有多高?直接问Cell本人就好了。直接法,就是把数据布局到Cell上,然后拿到Cell最底部控件的MaxY值。

第一步:创建Cell并正确设置约束,使文字区域高度能够根据文字内容多少自动调整

QQ截图20160329114325.png

添加好约束

第二步:再给这个Cell添加点别的东东,就叫这个东东BottomCub了。为Cub添加好约束。

QQ截图20160329114358.png

随便添加点什么

第三步:为这个Cell写一个返回Cell高度 - 也就是BottomCub最大Y值的方法

#import "TestCell.h"

@interface TestCell ()

@property (strong, nonatomic) IBOutlet UILabel *longLabel;

@property (strong, nonatomic) IBOutlet UIView *bottomCub;

@end

@implementation TestCell

//  Cell的构造方法

+ (instancetype)creatWithTitle :(NSString *)title inTableView :(UITableView *)tableView

{

TestCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(self)];

if (!cell) {

cell = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:kNilOptions].lastObject;

}

cell.longLabel.text = title;

return cell;

}

/**

*  拿到bottomCub的最大Y值并返回

*/

- (CGFloat)cellHeight

{

//  强制布局之前,需要先手动设置下cell的真实宽度,以便于准确计算

CGRect rect = self.frame;

rect.size.width = [[UIScreen mainScreen] bounds].size.width;

self.frame = rect;

[self layoutIfNeeded];    //  一定要强制布局下,否则拿到的高度不准确

return CGRectGetMaxY(self.bottomCub.frame);

}

@end

第四步:在代理方法中设置Cell高度

*注意:计算Cell高度的过程,一定不要放在heightForRow代理方法中!这一点在后文中将会有所提及。

#import "AskCellViewController.h"

#import "TestCell.h"

@interface AskCellViewController ()

@property (strong, nonatomic) UITableView *tableView;

/** 测试数据 - Cell中文字内容数组*/

@property(copy,nonatomic) NSArray *testTitleArray;

@end

@implementation AskCellViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self.view addSubview:self.tableView];

self.tableView.frame = self.view.bounds;

self.tableView.delegate = self;

self.tableView.dataSource = self;

self.tableView.tableFooterView = [[UIView alloc] init];

}

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

{

TestCell *cell = [TestCell creatWithTitle:self.testTitleArray[indexPath.row] inTableView:tableView];

cell.selectionStyle = UITableViewCellSelectionStyleNone;

return cell;

}

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

{

//  *注意:计算Cell高度的过程,一定不要放在此代理方法中!这一点在后文中将会有所提及,此处仅为演示方便

CGFloat cellHeight = [[TestCell creatWithTitle:self.testTitleArray[indexPath.row] inTableView:tableView] cellHeight];

NSLog(@"%f",cellHeight);

return cellHeight;

}

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

{

return self.testTitleArray.count;

}

#pragma mark - Lazy

- (UITableView *)tableView

{

if (!_tableView) {

_tableView = [[UITableView alloc] init];

}

return _tableView;

}

- (NSArray *)testTitleArray

{

return @[@"我是第一个Cell",@"我是第二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二个Cell",@"我是第三个Cell"];

}

@end

动态设定Cell高度结果

方案二:自己算(面向过程)

想知道妹纸爱你有多深?自己来算算看~

通常情况下,Cell之所以不等高,是因为Cell内部文字区域的高度会根据文字数量动态变化,图片区域的高度会根据图片数量而自动变化。也就是说,只要知道文字区域的高度、图片区域的高度,就可以硬生生计算出Cell的高度了。

注意注意注意:如果产品有可能会要求调整行距,切不可用此方法计算!

替代可选方案:

1

CGSize size = [cell.content sizeThatFits:CGSizeMake(cell.content.frame.size.width, MAXFLOAT)];

第一步:硬生生的将每个Cell的高度算出来,并保存在一个数组中

第二步:heightForRow方法中返回相应的CellHeight

#import "CalculatorViewController.h"

#import "TestCell.h"

@interface CalculatorViewController ()

@property (strong, nonatomic) UITableView *tableView;

/** 测试数据 - Cell中文字内容数组*/

@property(copy,nonatomic) NSArray *testTitleArray;

/** 用来存Cell高度的数组*/

@property(copy,nonatomic) NSArray *cellHeightArray;

@end

@implementation CalculatorViewController

- (void)viewDidLoad {

[super viewDidLoad];

[self.view addSubview:self.tableView];

self.tableView.frame = self.view.bounds;

self.tableView.delegate = self;

self.tableView.dataSource = self;

self.tableView.tableFooterView = [[UIView alloc] init];

}

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

{

TestCell *cell = [TestCell creatWithTitle:self.testTitleArray[indexPath.row] inTableView:tableView];

cell.selectionStyle = UITableViewCellSelectionStyleNone;

return cell;

}

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

{

CGFloat cellHeight = [self.cellHeightArray[indexPath.row] floatValue];

return cellHeight;

}

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

{

return self.testTitleArray.count;

}

#pragma mark - Lazy

- (UITableView *)tableView

{

if (!_tableView) {

_tableView = [[UITableView alloc] init];

}

return _tableView;

}

- (NSArray *)testTitleArray

{

return @[@"我是第一个Cell",@"我是第二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二个Cell",@"我是第三个Cell"];

}

- (NSArray *)cellHeightArray

{

NSMutableArray *cellHeightTMPArray = [@[] mutableCopy];

//  开始硬生生的计算每一个Cell高度

for (NSString *string in self.testTitleArray) {

CGFloat cellHeight = 0;

//  一个Cell由两部分组成 - 高度自动调整的Label & bottomCub

//  bottomCub高度是确定的 - 120,Label和bottomCub之间的间距是确定的 - 8

static CGFloat bottomCubHeight = 120;

static CGFloat bottomMargin = 8;

//  计算Label的高度 - 其实就是计算Lable中的String的总高度

//  1. 拿到将要放入Lable的String

NSString *stringForLabel = string;

//  2. 根据文字内容、字体(固定值)、文字区域最大宽度计算String总高度

static CGFloat fontSize = 17;

CGFloat labelHeight = [stringForLabel sizeWithFont:[UIFont systemFontOfSize:fontSize] constrainedToSize:CGSizeMake(self.tableView.frame.size.width, CGFLOAT_MAX)].height;

//  3. 拿到了总高度,放入数组

cellHeight = labelHeight + bottomMargin + bottomCubHeight;

[cellHeightTMPArray addObject:@(cellHeight)];

}

return cellHeightTMPArray;

}

@end

方案三:利用iOS 8新特性

想知道妹纸爱你有多深?知道这个干嘛,直接通过iOS8,让妹纸爱上你不就好啦~

其实,iOS8已经提供了直接通过XIB让Cell高度自适应的方法了,只要简单拖拖线,根本木有必要计算Cell高度,就可以搞定不等高Cell

第一步:设置tableView的估算Cell高度&rowHeight值为自动计算模式

- (void)viewDidLoad {

[super viewDidLoad];

self.tableView.estimatedRowHeight = 100;  //  随便设个不那么离谱的值

self.tableView.rowHeight = UITableViewAutomaticDimension;

}

第二步:为Cell中最下面的View设置约束 - 除了要定高、定宽、左上角粘着Label外,还要设置bottom距contentView的bottom间距为固定值,如0

bottomCub约束的添加方式

第三步:一定要注意 - 不能实现heightForRow代理方法!!!不能实现heightForRow代理方法!!!不能实现heightForRow代理方法!!!重要的事情说三遍...

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

{

return 1000;

}

QQ截图20160329114956.png

iOS8新特性实现Cell高度的自适应

效果:一样杠杠滴~

课题二:在哪计算Cell高度

方案一:在heightForRow代理方法中计算

示例代码:见课题一方案一

说明:在这里进行计算是非常糟糕的选择,因为系统调用heightForRow方法非常频繁 感兴趣的小伙伴可以打印测试下...在这里进行计算,意味着系统每调用一次heightForRow方法,就会执行一次高度计算...好可怕有木有?

方案二:在请求到数据后马上计算

示例代码:见课题一方案二

说明:在这里进行计算相对于方案一来说进步了很多,在这里计算是不错的选择哦!

方案三:在cellForRow代理方法中算

说明:其实,要隆重介绍的是方案三~

思路:

1.既然想知道Cell的高度,那么一定是Cell自己最懂自己有多高啦(面向对象的思维)。

2.那么,在哪里能拿到Cell和Cell的高度呢? - 当然是CellForRow代理方法中啦!

3.但是,在CellForRow中拿到Cell高度后,如何传递给heightForRow代理方法呢? - 可以将Cell高度保存在一个数组中,或者保存在Cell对应的Model中~

4.但是,我们知道系统对tableView代理方法的调用顺序,是先调取heightForRow再调取cellForRow的呀,这意味着,我们在cellForRow方法中拿到cell高度之前,就需要设置heightForRow...怎么办?

a.解决方案:实现estimatedHeightForRow代理方法!

b.实现这个代理方法后,系统会先调取cellForRow,再调取heightForRow,而且实现这个代理方法之后,腰不酸了,腿不疼了,一口气上五楼也不费劲了~

示例代码:可以参考下我之前的文章哦!传送门 - iOS项目实例:QQ聊天界面UI搭建

注意:如果实现了estimatedHeightForRow代理方法,可能会造成tableView的ContentSize值不正确哦!所以,该方法请选择使用...

结论

处理不等高TableViewCell,优先使用iOS8新特性(课题一方案三)

不能使用iOS8新特性的情况下,优先选择课题一方案一+课题二方案三组合

不能用上面两种,优先选择使用课题一方案一+课题二方案二组合~

补充

tableView的contentSize计算机制

实验方案

自定义一个tableView的子类,重写setContentSize方法,在该方法中打印contentSizeHeight,观察总结。

分两组:第一组实现estimatedHeightForRow方法,第二组不实现。

第一组:section = 1 rowNumber = 5 | 估算行高10 | 实际行高100

第二组:section = 1 rowNumber = 5 | 未实现估算行高 | 实际行高100

实验结果

第一组

36.png

实验一 - 1组5行|估算行高10|实际行高100|计算contentSize.gif

第二组

QQ截图20160329115440.png

实验二 - 1组5行|不估算|实际行高100|计算contentSize.gif

实验结论

viewDidAppear方法中拿到的contentSize才是准确的contentSize

contentSize至少会设置3次,如果估算行高与实际行高不符,会再次设置contentSize

未解之谜

通过打印我们可以看到,获取cell之前,诡异地对heighForRow方法遍历了三次...为什么是三次?

为什么最少设置3次contentSize,不能只设置1次吗?

能否把返回Cell的方法和heightForRow方法合并成一个代理方法?

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

推荐阅读更多精彩内容

  • UITableViewCell 父类是UIView UITableView的每一行都是一个UITableViewC...
    翻这个墙阅读 6,398评论 0 1
  • 我们在上一篇《通过代码自定义不等高cell》中学习了tableView的相关知识,本文将在上文的基础上,利用sto...
    啊世ka阅读 1,435评论 2 7
  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 8,834评论 3 38
  • 版权声明:未经本人允许,禁止转载. 1. TableView初始化 1.UITableView有两种风格:UITa...
    萧雪痕阅读 2,814评论 2 10
  • 在街道口,碰到一对情侣,男孩对女孩说:“以后,我会给你幸福的,请你相信我,好吗?” 突然,我就想到了这个段子...
    壹冰阅读 387评论 0 1