iOS之动态计算UITableViewCell高度技巧(一)

动态计算的UITableViewCell高度详解

说明:转载自Havens’blog http://www.ifun.cc/blog/2014/02/21/dong-tai-ji-suan-uitableviewcellgao-du-xiang-jie/

不知道大家有没有发现,在iOS APP开发过程中,UITableView是我们显示内容常见的控件,本人觉得它是UIKit中最复杂的一个控件。今天要向大家介绍的就是如何动态计算UITableViewCell高度的一经验与技巧,在此做一些总结方便朋友们查阅。

。为了不让讲解空洞抽象,我还是用代码实例的方式进行讲解,更这样容易接收与学习
。本文将介绍四种情况下的UITableViewCell的计算方式,分别是:

  1. 在UITableViewCell中使用UILabel进行自动布局
  2. 在UITableViewCell中使用UITextView进行自动布局
  3. 在UITableViewCell中使用UILabel进行手动布局
  4. 在UITableViewCell中使用UITextView进行手动布局
  5. 随的UITextView高度动态改变细胞高度

由于只是一个演示,所以命名这些都是随意从简。

首先创建一个单页的工程,我命名为CellHeightDemo

在UITableViewCell中使用UILabel自动布局

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

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

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

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

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

这儿有两点需要说明:1。的UILabel的属性线这儿设为了0表示显示多行0.2。自动布局一定要建立完整。

接着我们在UITableView中来使用我们自定义的UITableViewCell C1。
首先我们创建一个UITableViewController的子类T1ViewController,接着在Main.storyboard中拖入一个UITableViewController,并关联T1ViewController。


一切都准备好了,那我们现在来写点代码,给UITableView的加点料。
我们想要我们的UITableView的使用C1.xib中自定义的细胞,那么我们需要向的UITableView进行注册。

1 
2
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的协议:

1 
2 
3 
4 
5 
6 
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中的数据我们可以看到,每一个单元格显示的数据高度是不一样的,那么我们需要动态计算单元格的高度。由于是自动布局,所以我们需要用到一个新的API systemLayoutSizeFittingSize:来计算UITableViewCell所占空间高度.Cell的高度在于 - (CGFloat)tableView:(UITableView )tableView heightForRowAtIndexPath:(NSIndexPath)indexPath这个UITableViewDelegate的方法里面传给UITableView的。

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

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

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

然后初始化它:

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

下面是计算细胞高度的实现:

1 
2 
3 
4 
5 
6 
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的背景色充充为浅灰色下面是运行效果:

在UITableViewCell中使用UITextView自动布局

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

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

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

1 
2 
3 
4 
5 
6 
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的帧是不影响systemLayoutSizeFittingSize:的计算不知道这样说大家明白没。
所以,我们需要加上textViewSize.height。

下面是运行效果:

在UITableViewCell中使用UILabel手动布局

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

记得关闭C3.xib的自动布局


直接上代码了:

1 
2 
3 
4 
5 
6 
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方法:

1 
2 
3 
4 
5 
6 
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));
}

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

运行效果如图:

在UITableViewCell中使用UITextView进行手动布局

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

计得关闭C4.xib的自动布局


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

1 
2 
3 
4 
5 
6 
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的高度动态改变细胞高度

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

记得开启C5.xib的自动布局


先看代码:

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
- (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的高度重新计算,以达到目的。


本文只是简单的介绍了一些原理与技巧,之细节处还请参看源码

参考:
http : //www.howlin-interactive.com/2013/01/creating-a-self-sizing-uitextview-within-a-uitableviewcell-in-ios-6/ http://johnszumski.com/blog/ auto-layout-for-table-view-cells-with-dynamic-heights http://technet.weblineindia.com/mobile/add-auto-layout-support-for-uiscrollview-with-example-in-ios-app -development / http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值