课题一:如何计算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的几种方法的更多相关文章

  1. 关于iOS去除数组中重复数据的几种方法

    关于iOS去除数组中重复数据的几种方法   在工作工程中我们不必要会遇到,在数组中有重复数据的时候,如何去除重复的数据呢? 第一种:利用NSDictionary的AllKeys(AllValues)方 ...

  2. iOS 获取文件的目录路径的几种方法 [转]

    iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...

  3. IOS 点击空白处隐藏键盘的几种方法

    IOS 点击空白处隐藏键盘的几种方法     IOS7 点击空白处隐藏键盘的几种方法   IOS开发中经常要用到输入框,默认情况下点击输入框就会弹出键盘,但是必须要实现输入框return的委托方法才能 ...

  4. 处理不等高TableViewCell

    课题一:如何计算Cell高度 方案一:直接法(面向对象) 想知道妹纸爱你有多深?直接去问妹纸本人吧! 嗯!Cell也是一样的,想知道cell到底有多高?直接问Cell本人就好了.直接法,就是把数据布局 ...

  5. iOS: 让键盘消失的的4种方法

    转自:http://leopard168.blog.163.com/blog/static/168471844201422121310352/ 在iOS app中,只要用到编辑框(UITextFiel ...

  6. 【iOS开发】创建单例的两种方法

    创建一个单例很多办法.我先列举一个苹果官方文档中的写法. [cpp] view plaincopy   static AccountManager *DefaultManager = nil; + ( ...

  7. cocos2dx怎样设置ios和Android横屏竖屏的几种方法

    cocos2d-x编译到ios上.默认是横屏的,若要改为http://竖屏.不同的ios版本号.方法也会不同 在ios7上或许我们设置好了横竖屏.但到了ios6上或许会变化.以下白白给大家分享一下我的 ...

  8. IOS把图片缓存到本地的几种方法

    把图片缓存到本地,在很多场景都会用到,如果是只储存文字信息,那建一个plist文件,或者数据库就能很方便的解决问题,但是如果存图片到沙盒就没那么方便了.这里介绍两种保存图片到沙盒的方法. 一.把图片转 ...

  9. iOS 中使用 XIB 自定义cell 的两种方法 以及 编译出现常见 的错误 ++++(xcode6.0之后)

    一. 注册cell 1.创建自定义cell并勾选 xib :(勾选xib就会自动生成与cell文件关联的xib) 2.在 tableViewController里注册自定义Cell (或者遵守tabl ...

随机推荐

  1. OPEN CASCADE Gauss Least Square

    OPEN CASCADE Gauss Least Square eryar@163.com Abstract. The least square can be used to solve a set ...

  2. (转)使用minicpan创建本地CPAN

    在临时的办公场所网络不畅,有时不能下载cpan上的软件包,所有只能自建一个cpan. 这里使用了工具'minicpan',简单地说:就是把互联网上的CPAN搬到自己的电脑里,它的最初想法来自Randa ...

  3. 网络通信简单实例BIO,NIO,AIO

    这里,我将做一个简单的通信程序,分别使用三种原始的通信工具:BIO,NIO,AIO. 功能就是一个服务器,一个客户端.服务器就是处理请求,返回响应.而客户端就是连接服务器,发送请求,接收响应. 第一步 ...

  4. java 线程 Thread 使用介绍,包含wait(),notifyAll() 等函数使用介绍

    (原创,转载请说明出处!谢谢--http://www.cnblogs.com/linguanh/) 此文目的为了帮助大家较全面.通俗地了解线程 Thread 相关基础知识! 目录: --线程的创建: ...

  5. 前端项目构建之yeoman

    各位好啊,我又和大家见面了,也许你已经不记得大明湖畔的容嬷嬷,但是只要记得博客园中的我就好,希望我的博客能像一股清风,为你驱散炎热的酷暑,好了,废话不多说,开始上干货,我今天带给大家的是前端工程化开发 ...

  6. 1Z0-053 争议题目解析704

    1Z0-053 争议题目解析704 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 704.View the Exhibit and examine the data manipul ...

  7. JavaScript 中的相等检测

    一.有时JavaScript的等价比较是一件抓狂的事情,看看这个表格就一目了然了.

  8. CSS魔法堂:Box-Shadow没那么简单啦:)

    前言  说起box-shadow那第一个想法当然就是用来实现阴影,其实它还能用于实现其他好玩的效果的,本篇就打算说说box-shadow的那些事. 二话不说看效果 3D小球 <style typ ...

  9. [Web API] Web API 2 深入系列(2) 消息管道

    目录 HttpMessageHandler Web Host模式处理过程 Self Host模式处理过程 HttpMessageHandler Web API处理管道由一系列HttpMessageHa ...

  10. 实体生命周期【Entity Lifecycle】(EF基础系列10)

    Before we work on CRUD operation (Create, Read, Update, Delete), it's important to understand the en ...