iOS之处理不等高TableViewCell的几种方法
课题一:如何计算Cell高度
方案一:直接法(面向对象)
直接法,就是把数据布局到Cell上,然后拿到Cell最底部控件的MaxY值。
第一步:创建Cell并正确设置约束,使文字区域高度能够根据文字内容多少自动调整

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

随便添加点什么
第三步:为这个Cell写一个返回Cell高度 - 也就是BottomCub最大Y值的方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#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代理方法中!这一点在后文中将会有所提及。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
#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)]; |
(注于:2016.1.28)
第一步:硬生生的将每个Cell的高度算出来,并保存在一个数组中
第二步:heightForRow方法中返回相应的CellHeight
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
#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已经提供了直接通过XIB让Cell高度自适应的方法了,只要简单拖拖线,根本木有必要计算Cell高度,就可以搞定不等高Cell
第一步:设置tableView的估算Cell高度&rowHeight值为自动计算模式
|
1
2
3
4
5
6
|
- (void)viewDidLoad { [super viewDidLoad]; self.tableView.estimatedRowHeight = 100; // 随便设个不那么离谱的值 self.tableView.rowHeight = UITableViewAutomaticDimension;} |
第二步:为Cell中最下面的View设置约束 - 除了要定高、定宽、左上角粘着Label外,还要设置bottom距contentView的bottom间距为固定值,如0

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

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
实验结果
第一组

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

实验二 - 1组5行|不估算|实际行高100|计算contentSize.gif
实验结论
viewDidAppear方法中拿到的contentSize才是准确的contentSize
contentSize至少会设置3次,如果估算行高与实际行高不符,会再次设置contentSize
未解之谜
通过打印我们可以看到,获取cell之前,诡异地对heighForRow方法遍历了三次...为什么是三次?
为什么最少设置3次contentSize,不能只设置1次吗?
能否把返回Cell的方法和heightForRow方法合并成一个代理方法?
iOS之处理不等高TableViewCell的几种方法的更多相关文章
- 关于iOS去除数组中重复数据的几种方法
关于iOS去除数组中重复数据的几种方法 在工作工程中我们不必要会遇到,在数组中有重复数据的时候,如何去除重复的数据呢? 第一种:利用NSDictionary的AllKeys(AllValues)方 ...
- iOS 获取文件的目录路径的几种方法 [转]
iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...
- IOS 点击空白处隐藏键盘的几种方法
IOS 点击空白处隐藏键盘的几种方法 IOS7 点击空白处隐藏键盘的几种方法 IOS开发中经常要用到输入框,默认情况下点击输入框就会弹出键盘,但是必须要实现输入框return的委托方法才能 ...
- 处理不等高TableViewCell
课题一:如何计算Cell高度 方案一:直接法(面向对象) 想知道妹纸爱你有多深?直接去问妹纸本人吧! 嗯!Cell也是一样的,想知道cell到底有多高?直接问Cell本人就好了.直接法,就是把数据布局 ...
- iOS: 让键盘消失的的4种方法
转自:http://leopard168.blog.163.com/blog/static/168471844201422121310352/ 在iOS app中,只要用到编辑框(UITextFiel ...
- 【iOS开发】创建单例的两种方法
创建一个单例很多办法.我先列举一个苹果官方文档中的写法. [cpp] view plaincopy static AccountManager *DefaultManager = nil; + ( ...
- cocos2dx怎样设置ios和Android横屏竖屏的几种方法
cocos2d-x编译到ios上.默认是横屏的,若要改为http://竖屏.不同的ios版本号.方法也会不同 在ios7上或许我们设置好了横竖屏.但到了ios6上或许会变化.以下白白给大家分享一下我的 ...
- IOS把图片缓存到本地的几种方法
把图片缓存到本地,在很多场景都会用到,如果是只储存文字信息,那建一个plist文件,或者数据库就能很方便的解决问题,但是如果存图片到沙盒就没那么方便了.这里介绍两种保存图片到沙盒的方法. 一.把图片转 ...
- iOS 中使用 XIB 自定义cell 的两种方法 以及 编译出现常见 的错误 ++++(xcode6.0之后)
一. 注册cell 1.创建自定义cell并勾选 xib :(勾选xib就会自动生成与cell文件关联的xib) 2.在 tableViewController里注册自定义Cell (或者遵守tabl ...
随机推荐
- javascript 闭包
闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述 ...
- Qt Undo Framework Demo
Qt Undo Framework Demo eryar@163.com Abstract. Qt’s Undo Framework is an implementation of the Comma ...
- The Installation and Compilation of OpenCASCADE
OpenCASCADE的编译 The Installation and Compilation of OpenCASCADE eryar@163.com 一. 安装OpenCASCADE 可以从Ope ...
- 【原创】开源Math.NET基础数学类库使用(01)综合介绍
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...
- 【原创】开源Math.NET基础数学类库使用(07)常用的数学物理常数
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 1.前 ...
- 启动第一个 KVM 虚机 - 每天5分钟玩转 OpenStack(4)
本节演示如何使用 virt-manager 启动 KVM 虚机. 首先通过命令 virt-manager 启动图形界面 # virt-manager 点上面的图标创建虚机 给虚机命名为 kvm1,这里 ...
- CodeIgniter笔记-去掉index.php
1.修改Apache 的配置文件把LoadModule rewrite_module modules/mod_rewrite.so该行前的#去掉. 2.将 AllowOverride None ,修改 ...
- Spark入门实战系列--10.分布式内存文件系统Tachyon介绍及安装部署
[注]该系列文章以及使用到安装包/测试数据 可以在<倾情大奉送--Spark入门实战系列>获取 .Tachyon介绍 1.1 Tachyon简介 随着实时计算的需求日益增多,分布式内存计算 ...
- Cesium原理篇:6 Render模块(5: VAO&RenderState&Command)
VAO VAO(Vertext Array Object),中文是顶点数组对象.之前在<Buffer>一文中,我们介绍了Cesium如何创建VBO的过程,而VAO可以简单的认为是基于VBO ...
- Ubuntu下搭建ryu环境
RYU环境搭建总共四步: step1:首先下载相应的python套件,并且更新pip $ sudo apt-get install python-pip python-dev build-essent ...