博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
动态计算UITableViewCell高度详解
阅读量:5115 次
发布时间:2019-06-13

本文共 9000 字,大约阅读时间需要 30 分钟。

本文将介绍四种情况下UITableViewCell的计算方式,分别是:

1. Auto Layout with UILabel in UITableViewCell

创建一个空的xib,命名为C1.xib, 然后拖入一个UITableViewCell控件。接着创建一个UITableViewCell的子类,命名为C1类。然后在C1.xib中,将与C1类进行关联。别给我说你不会关联,如果不会那看下图你就明白了。V^

只需要在Class那里写入关联的类名C1即可。

还有由于UITableViewCell需要重用功能,所以我们还需要设置一个重用标识

在Identifier那里写入重用标识C1,当然你也可以用任意字符。不过后面代码里需要这个字符。

接着我们来布局。用到了auto layout, 在此我不想介绍auto layout, 以后有时间再专门介绍,下图就是我布局

这儿有两点需要说明:1. UILabel的属性Lines这儿设为了0表示显示多行。2. Auto Layout一定要建立完完整。

接着我们在UITableView中来使用我们自定义的UITableViewCell C1.

首先我们创建一个UITableViewController的子类T1ViewController, 接着在Main.storyboard中拖入一个UITableViewController,并关联T1ViewController.

一切都准备好了,那我们现在来写点代码,给UITableView加点料。

我们想要我们的UITableView使用C1.xib中自定义的Cell,那么我们需要向UITableView进行注册。

12
UINib *cellNib = [UINib nibWithNibName:@"C1" bundle:nil];[self.tableView registerNib:cellNib forCellReuseIdentifier:@"C1"];

这样就进行注册了,接着我们还需要每行显示的数据,为了简单一点,我就声明了一个NSArray变量来存放数据。

1
self.tableData = @[@"1\n2\n3\n4\n5\n6", @"123456789012345678901234567890", @"1\n2", @"1\n2\n3", @"1"];

现在实现UITableViewDataSource的protocol:

123456 7 8 9 10 11 12
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{    // Return the number of rows in the section.    return self.tableData.count;} - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { C1 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C1"]; cell.t.text = [self.tableData objectAtIndex:indexPath.row]; return cell; }

从self.tableData中的数据我们可以看到,每一个Cell显示的数据高度是不一样的,那么我们需要动态计算Cell的高度。由于是auto layout,所以我们需要用到一个新的API systemLayoutSizeFittingSize:来计算UITableViewCell所占空间高度。Cell的高度是在- (CGFloat)tableView:(UITableView )tableView heightForRowAtIndexPath:(NSIndexPath )indexPath这个UITableViewDelegate的方法里面传给UITableView的。

这里有一个需要特别注意的问题,也是效率问题。UITableView是一次性计算完所有Cell的高度,如果有1W个Cell,那么- (CGFloat)tableView:(UITableView )tableView heightForRowAtIndexPath:(NSIndexPath )indexPath就会触发1W次,然后才显示内容。不过在iOS7以后,提供了一个新方法可以避免这1W次调用,它就是- (CGFloat)tableView:(UITableView )tableView estimatedHeightForRowAtIndexPath:(NSIndexPath )indexPath。要求返回一个Cell的估计值,实现了这个方法,那只有显示的Cell才会触发计算高度的protocol. 由于systemLayoutSizeFittingSize需要cell的一个实例才能计算,所以这儿用一个成员变量存一个Cell的实列,这样就不需要每次计算Cell高度的时候去动态生成一个Cell实例,这样即方便也高效也少用内存,可谓一举三得。

我们声明一个存计算Cell高度的实例变量:

1
@property (nonatomic, strong) UITableViewCell *prototypeCell;

然后初始化它:

1
self.prototypeCell  = [self.tableView dequeueReusableCellWithIdentifier:@"C1"];

下面是计算Cell高度的实现:

123456 7
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    C1 *cell = (C1 *)self.prototypeCell;    cell.t.text = [self.tableData objectAtIndex:indexPath.row];    CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; NSLog(@"h=%f", size.height + 1); return 1 + size.height; }

看了代码,可能你有点疑问,为何这儿要加1呢?笔者告诉你,如果不加1,结果就是错误的,Cell中UILabel将显示不正确。原因就是因为这行代码CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];由于是在cell.contentView上调用这个方法,那么返回的值将是contentView的高度,UITableViewCell的高度要比它的contentView要高1,也就是它的分隔线的高度。如果你不相信,那请看C1.xib的属性,比较下面两张图。

发现没Cell的高度是127, 面contentView的高度是126, 这下明白了吧。

为了让读者看清楚,我将Cell中UILabel的背景色充为了light gray.下面是运行效果:

2. Auto Layout with UITextView in UITableViewCell

本小段教程将介绍UITextView在cell中计算高度需要注意的地方。同样参考上面我们创建一个C2.xib, UITableViewCell的子类C2,并关联C2.xib与C2类。并在C2.xib中对其布局,同样使用了auto layout. 布局如下图:

创始UITableViewController的了类T2ViewController,在Main.storyboard中拖入UITableViewController,并关联他们。接着代码中注册C2.xib到UITableView.

下面计是计算高度的代码:

123456 7 8 9 10
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {    C2 *cell = (C2 *)self.prototypeCell;    cell.t.text = [self.tableData objectAtIndex:indexPath.row];    CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; CGSize textViewSize = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; CGFloat h = size.height + textViewSize.height; h = h > 89 ? h : 89; //89是图片显示的最低高度, 见xib NSLog(@"h=%f", h); return 1 + h; }

在这儿我们是通过sizeThatFits:计算的UITextView的高度(这是计算UITextView内容全部显示时的方法,在第四小段中我们还会用到它),然后加上systemLayoutSizeFittingSize:返回的高度。为什么要这样呢? 因为UITextView内容的高度不会影响systemLayoutSizeFittingSize计算。这句话什么意思呢?我真不知道如何用言语表达了。还是先上一张图吧:

此图中距顶的约束是10, 距底的约束8, 距左边约束是87,距右边的约束是13, 那么systemLayoutSizeFittingSize:返回的CGSize为height等于19, size等于100. 它UITextView的frame是不影响systemLayoutSizeFittingSize:的计算。不知道这样说大家明白没。
所以,我们需要加上textViewSize.height. 

下面是运行效果:

3. Manual Layout with UILabel in UITableViewCell

本小段教程将介绍UILabel在Manual layout cell中计算高度, 原理是根据字体与字符串长度来计算长度与宽度。 按照前面介绍的,我们需要创建C3.xib, C3类, T3ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C3.xib中我就不加padding之类的了,如图

记得关闭C3.xib的auto layout

直接上代码了:

123456 7 8 9 10 11 12 13 14 15 16 17 18
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    C3 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C3"];    cell.t.text = [self.tableData objectAtIndex:indexPath.row]; [cell.t sizeToFit]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { C3 *cell = (C3 *)self.prototypeCell; NSString *str = [self.tableData objectAtIndex:indexPath.row]; cell.t.text = str; CGSize s = [str calculateSize:CGSizeMake(cell.t.frame.size.width, FLT_MAX) font:cell.t.font]; CGFloat defaultHeight = cell.contentView.frame.size.height; CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; NSLog(@"h=%f", height); return 1 + height; }

这儿用到了一个NSString的Cagetory方法:

123456 7 8 9 10 11 12 13 14 15 16 17 18
- (CGSize)calculateSize:(CGSize)size font:(UIFont *)font {    CGSize expectedLabelSize = CGSizeZero;        if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) { NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle.copy}; expectedLabelSize = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; } else { expectedLabelSize = [self sizeWithFont:font constrainedToSize:size lineBreakMode:NSLineBreakByWordWrapping]; } return CGSizeMake(ceil(expectedLabelSize.width), ceil(expectedLabelSize.height)); }

原理上面我已说了,这儿没有什么好说明的,代码一目了然。

运行效果如图:

4. Manual Layout with UITextView in UITableViewCell

本小段教程将介绍UITextView在Manual layout cell中计算高度, 原理是与第二小节里的相同,用sizeThatFits:的方法计算UITextView的长度与高度。然后加上padding就是Cell的高度。 按照前面介绍的,我们需要创建C4.xib, C4类, T4ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C4.xib中我就不加padding之类的了,如图

计得关闭C4.xib的auto layout

也直接上代码了,直观明了:

123456 7 8 9 10 11 12 13 14 15 16 17
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    C4 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C4"];    cell.t.text = [self.tableData objectAtIndex:indexPath.row]; [cell.t sizeToFit]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { C4 *cell = (C4 *)self.prototypeCell; NSString *str = [self.tableData objectAtIndex:indexPath.row]; cell.t.text = str; CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; CGFloat defaultHeight = cell.contentView.frame.size.height; CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; return 1 + height; }

运行效果:

 

5.随UITextView高度动态改变Cell高度

本小节要介绍的一个功能是,UITextView中UITableViewCell中,当输入UITextView中的字变多/变少时,高度变化,Cell高度与随之变化的功能。

按照前面介绍的,我们需要创建C5.xib, C5类, T5ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C5.xib中我就不加padding之类的了,如图

记得开启C5.xib的auto layout

先看代码:

123456 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
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{    C5 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C5"];    cell.t.text = @"123"; cell.t.delegate = self; return cell; } #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { C5 *cell = (C5 *)self.prototypeCell; cell.t.text = self.updatedStr; CGSize s = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; CGFloat defaultHeight = cell.contentView.frame.size.height; CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; return 1 + height; } #pragma mark - UITextViewDelegate - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { if ([text isEqualToString:@"\n"]) { NSLog(@"h=%f", textView.contentSize.height); } return YES; } - (void)textViewDidChange:(UITextView *)textView { self.updatedStr = textView.text; [self.tableView beginUpdates]; [self.tableView endUpdates]; }

原理就是UITextView内容改变的时候,计算自身高度,然后通知UITableView更新,这样就会触发UITableViewCell高度重新计算,以达到目的。

转载于:https://www.cnblogs.com/fengmin/p/5219435.html

你可能感兴趣的文章
html静态页面实现微信分享思路
查看>>
网站优化JS css压缩
查看>>
INCEPTION 编译过程报错 make[2]: * [sql/CMakeFiles/sql.dir/sql_yacc.cc.o] Error
查看>>
多线程操作SQLite注意事项
查看>>
洛谷P1776 宝物筛选
查看>>
http和https
查看>>
ORACLE常用函数汇总【转】
查看>>
2018.8.2 python中is和==的区别
查看>>
Linux解压 tar命令
查看>>
ls 知识点
查看>>
copy
查看>>
Oracle中包的创建
查看>>
python入门_老男孩_数据类型简介_int/bool/str转换_字符串索引和切片_字符串操作_day3...
查看>>
mysql 查询之聚合查询
查看>>
关于χ²分布和统计
查看>>
[sublime] sublime 实现Markdown编辑器
查看>>
【洛谷1962】 斐波那契数列
查看>>
THUSC2019去不了记
查看>>
JAVA面向对象——继承
查看>>
I/O重定向的原理和实现
查看>>