动态计算cell高度(1)
2015-08-19 15:55
411 查看
原文地址:http://www.mgenware.com/blog/?p=507
首先需要在Storyboard中创建好
Cells中设计好Cell界面。
接着,定义好Autolayout,注意Autolayout一定要在上下都绑定控件的位置,不要只从上到下定义,只有正确定义Autolayout,后面我们用到的
如下图:
Xcode会提示Autolayout的各种Ambiguity,提示修改控件的Compression resistance,如下图:
这里让Xcode智能修正就可以了,具体哪个控件的Compression resistance无所谓,因为我们最终的目的是让
然后,因为是在Xcode 5 iOS 7模式下设计的Storyboard,所以在iOS 7下运行肯定是没问题的:
接着在iOS 6上运行:
什么情况?出现这个问题的原因是:iOS 7和iOS 6中的许多控件默认高度都是不一样的,在其他普通UIView下,有了Autolayout,控件当然会正确显示。但是
来慢慢看问题,首先,如何测量使用Autolayout的
接着,另一个问题来了,是关于
我们来再看本例代码,首先在
在
然后把Cell加载数据的逻辑写在一个方法里,这个方法是被
接下来是关键的
然后是
读者可以看到,上面代码里,我在
OK,现在再次运行程序,即便是你Storyboard中把Cell高度手动调整成这样:
在iOS 6下会显示出正确的结果:
iOS 7下也下一样:
最后Log的信息,在1000个数据源的情况下,运行程序后没有进行任何滚动操作:
源代码下载
mgen_tableViewCellHeight.zip
源代码环境:Xcode 6.0
首先需要在Storyboard中创建好
TableViewController,使用动态Cell,在Prototype
Cells中设计好Cell界面。
接着,定义好Autolayout,注意Autolayout一定要在上下都绑定控件的位置,不要只从上到下定义,只有正确定义Autolayout,后面我们用到的
systemLayoutSizeFittingSize方法才会返回正确的结果。
如下图:
Xcode会提示Autolayout的各种Ambiguity,提示修改控件的Compression resistance,如下图:
这里让Xcode智能修正就可以了,具体哪个控件的Compression resistance无所谓,因为我们最终的目的是让
UITabelViewCell的高度去适合所有控件的大小。
然后,因为是在Xcode 5 iOS 7模式下设计的Storyboard,所以在iOS 7下运行肯定是没问题的:
接着在iOS 6上运行:
什么情况?出现这个问题的原因是:iOS 7和iOS 6中的许多控件默认高度都是不一样的,在其他普通UIView下,有了Autolayout,控件当然会正确显示。但是
UITableViewCell的高度是通过
UITableView的
heightForRowAtIndexPath方法来返回的。默认情况下,它是保持不变的。所以当Cell内控件的高度发生变化后,如果Cell高度没有因此而作出调整,肯定会出问题的。
来慢慢看问题,首先,如何测量使用Autolayout的
UIView的尺寸?可以使用
UIView的
systemLayoutSizeFittingSize方法,对于
UITableViewCell,那就是测量其
contentView的大小。那么,本例中需要返回Autolayout的Cell的高度,则可以写一个辅助方法,这样:
- (CGFloat)getCellHeight:(UITableViewCell*)cell { [cell layoutIfNeeded]; [cell updateConstraintsIfNeeded]; CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height; return height; }
接着,另一个问题来了,是关于
UITableView的Cell重用机制。我们必须在
UITableView的
heightForRowAtIndexPath方法中返回根据Cell内容计算出来的动态高度,对于使用Autolayout的Cell,必须创建这个Cell才可以获取他的动态高度。而经测试发现,如果在
heightForRowAtIndexPath调用Cell重用方法,也就是
dequeueReusableCellWithIdentifier方法,Cell还是会被重复创建,也就是说Cell的重用机制在
heightForRowAtIndexPath中是无效的。解决方案是,在
heightForRowAtIndexPath只创建一个Cell,这个Cell专门用作测量所有Cell的高度,然后在
cellForRowAtIndexPath继续使用Cell重用逻辑就可以。
我们来再看本例代码,首先在
TableViewController中加入必要的字段,声明数据源,加入测试数据等,这些都是很简单的内容,不需要多讲:
//测试数据源 NSMutableArray *_dataSource;
在
viewDidLoad中初始化相关数据:
//viewDidLoad 初始化 _dataSource = [NSMutableArray arrayWithArray:@[@"Mgen", @"Tony", @"Jerry", @"一二三"]];
然后把Cell加载数据的逻辑写在一个方法里,这个方法是被
heightForRowAtIndexPath和
cellForRowAtIndexPath方法所共用的,因为不管是测量Cell的高度还是展示Cell,我们都需要Cell加载相应的数据:
- (void)loadCellContent:(MyCell*)cell indexPath:(NSIndexPath*)indexPath { //这里把数据设置给Cell cell.titleLabel.text = [_dataSource objectAtIndex:indexPath.row]; }
接下来是关键的
heightForRowAtIndexPath方法,这里的逻辑上面已经讲过,不需要用Cell重用机制,我们只创建一个Cell,利用这个Cell,不停地加载内容,然后返回高度就可以了,这两个步骤的辅助方法上面也都有,我们直接用,如下代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { //log [[GlobalCounter getInstance] add:@"get height"]; //只创建一个cell用作测量高度 static MyCell *cell = nil; if (!cell) cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"]; [self loadCellContent:cell indexPath:indexPath]; return [self getCellHeight:cell]; }
然后是
cellForRowAtIndexPath方法,这里调用
dequeueReusableCellWithIdentifier进行Cell重用就然后加载Cell内容就可以了:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //log [[GlobalCounter getInstance] add:@"get cell"]; static NSString *CellIdentifier = @"MyCell"; //注意在heightForRowAtIndexPath:indexPath无法使用dequeueReusableCellWithIdentifier:forIndexPath: MyCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //用dequeueReusableCellWithIdentifier:就得判断Cell为nil的情况 //如果在Storyboard中Prototype Cells中设置了具体Table View Cell的Identifier也是"MyCell"(也就是重用ID),那这里不会有返回nil的情况 if (!cell) { cell = [[MyCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } [self loadCellContent:cell indexPath:indexPath]; return cell; }
读者可以看到,上面代码里,我在
heightForRowAtIndexPath和
cellForRowAtIndexPath方法里都打了Log,还有一处需要打Log的地方,就是Cell本身的创建上,注意Storyboard中
UITableViewCell的创建是在
initWithCoder方法中的,而不是
initWithStyle:reuseIdentifier方法里的。
- (instancetype)initWithCoder:(NSCoder *)coder { [[GlobalCounter getInstance] add:@"create cell"]; return [super initWithCoder:coder]; }
OK,现在再次运行程序,即便是你Storyboard中把Cell高度手动调整成这样:
在iOS 6下会显示出正确的结果:
iOS 7下也下一样:
最后Log的信息,在1000个数据源的情况下,运行程序后没有进行任何滚动操作:
"create cell" = 7; //创建Cell 7次 "get cell" = 6; //调用heightForRowAtIndexPath 6次 "get height" = 2006; //调用cellForRowAtIndexPath 2006次
源代码下载
mgen_tableViewCellHeight.zip
源代码环境:Xcode 6.0
相关文章推荐
- java并发编程-Executor框架
- 关于Bean的知识private static final long serialVersionUID = 1L
- 常见26个jquery使用技巧详解(比如禁止右键点击、隐藏文本框文字等)
- C-1 九九乘法表
- 我的eclipse插件推荐
- 用CImage类来显示PNG、JPG等图片
- Android(java)学习笔记178:BroadcastReceiver之 自定义广播
- OpenGL ES 2.0 系列一
- HDU 1158 Employment Planning
- scala学习第二章 控制结构与函数
- poj1860 最短路 无限增大环
- QNetAccessManager http请求
- Using mysql as Nosql ------------- Mysql HandlerSockets使用
- 4.3 现在可用的客体类有哪些呢
- SuperMapPy 批量拼接 GeoTiff影像
- 挣值管理 EVM
- bootstrap -- 一个标签中,同时有 col-xs , col-sm , col-md , col-lg
- docker简介
- MyBatis <set>标签的使用
- c++ primer plus 第八章《编程题8.8.4》