`
啸笑天
  • 浏览: 3465584 次
  • 性别: Icon_minigender_1
  • 来自: China
社区版块
存档分类
最新评论

动态计算UITableViewCell高度详解

 
阅读更多

转自Haven's Blog

 
不知道大家有没有发现,在iOS APP开发过程中,UITableView是我们显示内容常见的控件,本人觉得它是UIKit中最复杂的一个控件。今天要向大家介绍的就是如何动态计算UITableViewCell高度的一经验与技巧,在此做一些总结方便朋友们查阅。为了不让讲解空洞抽象,我还是用代码实例的方式进行讲解,这样更容易接收与学习。
 本文将介绍四种情况下UITableViewCell的计算方式,分别是:
1. Auto Layout with UILabel in UITableViewCell
2. Auto Layout with UITextView in UITableViewCell
3. Manual Layout with UILabel in UITableViewCell
4. Manual Layout with UITextView in UITableViewCell
5. 随UITextView高度动态改变Cell高度
 
首先创建一个Single Page的工程,我命名为CellHeightDemo
 
1. Auto Layout with UILabel in UITableViewCell
创建一个空的xib,命名为C1.xib, 然后拖入一个UITableViewCell控件。接着创建一个UITableViewCell的子类,命名为C1类。然后在C1.xib中,将与C1类进行关联。别给我说你不会关联,如果不会那看下图你就明白了。 
\

 
 
只需要在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进行注册。
  1. UINib *cellNib = [UINib nibWithNibName:@"C1" bundle:nil]; 
  2. [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:
  1. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
  2.     // Return the number of rows in the section. 
  3.     return self.tableData.count; 
  4.   
  5. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  6.     C1 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C1"]; 
  7.     cell.t.text = [self.tableData objectAtIndex:indexPath.row]; 
  8.     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高度的实现:
  1. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  2.     C1 *cell = (C1 *)self.prototypeCell; 
  3.     cell.t.text = [self.tableData objectAtIndex:indexPath.row]; 
  4.     CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 
  5.     NSLog(@"h=%f", size.height + 1); 
  6.     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.
 
下面计是计算高度的代码:
  1. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  2.     C2 *cell = (C2 *)self.prototypeCell; 
  3.     cell.t.text = [self.tableData objectAtIndex:indexPath.row]; 
  4.     CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; 
  5.     CGSize textViewSize = [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; 
  6.     CGFloat h = size.height + textViewSize.height; 
  7.     h = h > 89 ? h : 89;  //89是图片显示的最低高度, 见xib 
  8.     NSLog(@"h=%f", h); 
  9.     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 


 
 
直接上代码了:
  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  2.     C3 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C3"]; 
  3.     cell.t.text = [self.tableData objectAtIndex:indexPath.row]; 
  4.     [cell.t sizeToFit]; 
  5.     return cell; 
  6.   
  7. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  8.     C3 *cell = (C3 *)self.prototypeCell; 
  9.     NSString *str = [self.tableData objectAtIndex:indexPath.row]; 
  10.     cell.t.text = str; 
  11.     CGSize s = [str calculateSize:CGSizeMake(cell.t.frame.size.width, FLT_MAX) font:cell.t.font]; 
  12.     CGFloat defaultHeight = cell.contentView.frame.size.height; 
  13.     CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; 
  14.     NSLog(@"h=%f", height); 
  15.     return 1  + height; 
 
这儿用到了一个NSString的Cagetory方法:
  1. - (CGSize)calculateSize:(CGSize)size font:(UIFont *)font { 
  2.     CGSize expectedLabelSize = CGSizeZero; 
  3.      
  4.     if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) { 
  5.         NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 
  6.         paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping; 
  7.         NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle.copy}; 
  8.          
  9.         expectedLabelSize = [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size; 
  10.     } 
  11.     else { 
  12.         expectedLabelSize = [self sizeWithFont:font 
  13.                                        constrainedToSize:size 
  14.                                            lineBreakMode:NSLineBreakByWordWrapping]; 
  15.     } 
  16.   
  17.     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 


 
 
也直接上代码了,直观明了:
  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  2.     C4 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C4"]; 
  3.     cell.t.text = [self.tableData objectAtIndex:indexPath.row]; 
  4.     [cell.t sizeToFit]; 
  5.     return cell; 
  6.   
  7. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  8.     C4 *cell = (C4 *)self.prototypeCell; 
  9.     NSString *str = [self.tableData objectAtIndex:indexPath.row]; 
  10.     cell.t.text = str; 
  11.     CGSize s =  [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; 
  12.     CGFloat defaultHeight = cell.contentView.frame.size.height; 
  13.     CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; 
  14.     return 1  + height; 
  15.   
 
运行效果: 


 
 
5. 随UITextView高度动态改变Cell高度
本小节要介绍的一个功能是,UITextView中UITableViewCell中,当输入UITextView中的字变多/变少时,高度变化,Cell高度与随之变化的功能。
按照前面介绍的,我们需要创建C5.xib, C5类, T5ViewController类,Main.storyboard中拖入UITableViewController,并分别建立关联。 为了简单,C5.xib中我就不加padding之类的了,如图
 
记得开启C5.xib的auto layout 


 
 
先看代码:
  1. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
  2.     C5 *cell = [self.tableView dequeueReusableCellWithIdentifier:@"C5"]; 
  3.     cell.t.text = @"123"
  4.     cell.t.delegate = self; 
  5.     return cell; 
  6.  
  7. #pragma mark - UITableViewDelegate 
  8. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 
  9.     C5 *cell = (C5 *)self.prototypeCell; 
  10.     cell.t.text = self.updatedStr; 
  11.     CGSize s =  [cell.t sizeThatFits:CGSizeMake(cell.t.frame.size.width, FLT_MAX)]; 
  12.     CGFloat defaultHeight = cell.contentView.frame.size.height; 
  13.     CGFloat height = s.height > defaultHeight ? s.height : defaultHeight; 
  14.     return 1  + height; 
  15.  
  16. #pragma mark - UITextViewDelegate 
  17. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { 
  18.     if ([text isEqualToString:@"\n"]) { 
  19.         NSLog(@"h=%f", textView.contentSize.height); 
  20.     } 
  21.     return YES; 
  22.  
  23. - (void)textViewDidChange:(UITextView *)textView { 
  24.     self.updatedStr = textView.text; 
  25.     [self.tableView beginUpdates]; 
  26.     [self.tableView endUpdates]; 
 
原理就是UITextView内容改变的时候,计算自身高度,然后通知UITableView更新,这样就会触发UITableViewCell高度重新计算,以达到目的。 
 
本文只是简单的介绍了一些原理与技巧,细节之处还请参看源码 
 
参考:
  
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 24.4 KB
  • 大小: 17 KB
  • 大小: 106.2 KB
  • 大小: 72.4 KB
  • 大小: 61.1 KB
  • 大小: 78.5 KB
  • 大小: 28.4 KB
  • 大小: 115.1 KB
  • 大小: 20.4 KB
  • 大小: 24.9 KB
  • 大小: 70.1 KB
  • 大小: 22.5 KB
  • 大小: 69.4 KB
  • 大小: 25.1 KB
  • 大小: 78.2 KB
分享到:
评论
4 楼 啸笑天 2015-07-15  
使用下面两行代码替代 estimatedHeightForRowAtIndexPath 实现了自动计算 UITableViewCell 高度
        // 经过测试,实际表现及运行效率均相似,大
3 楼 啸笑天 2015-07-07  
http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/ 优化UITableViewCell高度计算的那些事
2 楼 啸笑天 2015-07-07  
http://blog.csdn.net/iunion/article/details/12185077#0-tsina-1-7133-397232819ff9a47a7b7e80a40613cfe1   TextKit学习(四)通过boundingRectWithSize:options:attributes:context:计算文本尺寸
1 楼 啸笑天 2015-07-07  
http://www.cocoachina.com/ios/20141218/10687.html iOS 8 自适应 Cell

相关推荐

    IOS开发UITableViewCell自定义那点事.pdf

    这可以通过设置`numberOfLines`为0来实现文本自动换行,并结合`sizeToFit`方法来自动调整UILabel的大小,最后根据UILabel的实际高度来动态计算UITableViewCell的高度。示例如下: ```objective-c - ...

    ios-Autolayout.zip

    通过一行代码,开发者就可以快速实现各种复杂的界面布局需求,尤其在处理动态高度的表格视图时,能够极大地提高开发效率。 **高度自适应** 在iOS应用中,尤其是涉及到列表展示时,单元格的高度自适应是一项重要...

    UITableView优化技巧

    可以通过预定义固定高度或根据内容动态计算的方式来实现。 - 如果单元格高度依赖于内容,可以在数据模型发生变化时预先计算好高度,并保存在数据模型中。 #### 四、其他优化技巧 1. **减少计算量**:尽可能减少在...

    tableView用法详解

    5. **性能优化**:`tableView`的性能优化主要包括减少内存分配(通过复用`cell`)、异步加载数据(如图片)、以及合理计算高度(避免提前计算大量未显示的`cell`高度)。`estimatedRowHeight`和`...

    新浪微博动态列表

    【标题】:“新浪微博动态列表”实现详解 在iOS开发中,设计并实现一个类似新浪微博的动态列表是一项常见的任务,这涉及到用户界面UI的设计以及数据结构和网络请求的处理。SamuelandKevin创建的HKPTimeLine项目就是...

    iOS如何让tableview支持不同种类的cell详解

    在这些子类中,可以重写一些特定的方法,比如计算高度 (`heightForRowAtIndexPath`) 和展示视图 (`awakeFromNib` 或 `layoutSubviews`)。 3. 数据源(Datasource)需要提供一个方法来判断给定索引路径(IndexPath)...

    iOS中TableView如何统一数据源代理详解

    在iOS开发中,UITableView是用于显示列表数据的关键组件,它依赖于数据源代理模式来管理内容。为了提高代码复用性和...同时,还可以根据需要扩展`CCDataSource`以支持更复杂的功能,如动态cell高度计算、动画效果等。

    详解iOS App中UITableView的创建与内容刷新

    对于复杂的表格布局,可以使用`estimatedRowHeight`和`rowHeight`属性来实现自动高度计算,提高性能。此外,还可以通过`numberOfSectionsInTableView:`和`tableView:numberOfRowsInSection:`方法实现分组列表。 在...

    IphoneTreeView.rar

    在`-tableView:heightForRowAtIndexPath:`中根据节点是否展开计算Cell的高度。 4. **点击事件处理**: `-tableView:didSelectRowAtIndexPath:`方法中,处理用户点击Cell的事件。当用户点击时,更新展开状态并刷新...

    斯坦福大学开放课程:iOS开发教程2010年秋(Lecture 10)

    #### 二、UITableView详解 1. **定义与特性**: - `UITableView`是一种非常高效的数据展示方式,即使面对大量的数据集也能保持良好的性能。 - 它一次只能展示一列数据,但可以通过嵌套表格视图来展示分层数据集。...

    UICollectionViewDemo

    UICollectionView是iOS开发中不可或缺的一部分,它为开发者提供了展示大量数据的灵活方式,具有高度可定制化的布局和交互。本篇将深入探讨在Objective-C中如何有效地利用UICollectionView及其核心概念。 一、...

    CollectionViewDemo:集合视图演示

    【UICollectionView详解:集合视图演示】 集合视图(UICollectionView)是iOS开发中一个重要的界面组件,主要用于展示可滚动的数据集合,通常用于创建网格布局、瀑布流等复杂布局。本项目"CollectionViewDemo"就是...

Global site tag (gtag.js) - Google Analytics