AutoLayout深入浅出五[UITableView动态高度]
本文转载至 http://grayluo.github.io//WeiFocusIo/autolayout/2015/02/01/autolayout5/
我们经常会遇到UITableViewCell的高度要跟随内容而调整,在未引入AutoLayout之前,我们使用以下方法计算Label高度,然后heightForRowAtIndexPath中返回计算的高度,这种做法,真的很土很局限很不好,如果UILabel使用了CoreText或者UIKit进行了富文本不同字体的排版,它更是没办法,我还得分段来计算,总之各种麻烦。
- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(NSLineBreakMode)lineBreakMode NS_DEPRECATED_IOS(2_0, 7_0, "Use -boundingRectWithSize:options:attributes:context:"); // NSTextAlignment is not needed to determine size
本系列文章我们讨论的是AutoLayout,那iOS6引入AutoLayout之后,情况是否有所变化呢?当然!而且AutoLayout在iOS不断更新过程中,也在一起不断的优化,以方便开发者进行布局。说实话,跟很多开发者一样,我目前也并不是特别喜欢AutoLayout,有一些不可控的因素,布局并没有完全掌握在自己手上,需要依赖系统根据约束进行调整,这让保守的开发人员很没有安全感。不废话了,我们还是进入到正题。
我们直接拿一个现实的需求来进行讨论,如下图,我们需要构建一个页面,上面部分显示我们预约保养的基本信息,下面部分显示该门店目前提供的优惠券列表,这种需求最简单的做法就是直接用两种UITableViewCell,下面的部分的UITableViewCell要简单一些,高度固定,而上面部分的UITableViewCell的内容有很多不确定的内容,比如用户预约保养选择的项目,门店的名称地址,这些UILabel的高度都不确定,所以导致上面部分的UITableViewCell的高度需要动态调整,这是一个比较典型的实例,我们一起来看一下如何解决。
一、建立合理的约束
我们先建立自定义Cell: AppointmentedInfoCell (创建XIB)。 然后设置合理的约束条件,什么是合理的约束条件,一方面我们需要按前面讲到的设置正确的约束条件,另一方面我的意思主要是控件的compression resistance 和 hugging constraints ,在IB中如下图:
我们知道在Autolayout中,我们的UILabel,UIButton等控件都有了内建大小(intrinsic content size),就是说控件的大小会根据内容进行自动调整,可以将这些控件的大小和ScrollView的bounds和contentSize进行对比,意思有点类似,只不过UILabel,UIButton这些控件并不像Scrollview一样可以在bounds不等于contentSize的情况下进行滚动查看内容。 在这里为了使用UILabel的内建大小,我们要保持compression resistance 和 hugging constraints 的垂直方向优先级没有被更高的优先级所覆盖,比如更改了UILabel内建大小的优先级(priority),并设置了UILabel的高度约束的优先级高于内建大小的优先级,那内建大小自然就不起作用了,就会以高优先级为准.
- 下面是官方关于intrinsic content size的说明: Custom views typically have content that they display of which the layout system is unaware. Overriding this method allows a custom view to communicate to the layout system what size it would like to be based on its content. This intrinsic size must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height, for example. Editing Auto Layout Constraints
一方面我们确保了AppointmentedInfoCell中的控件,目前全是UILabel,其内建大小垂直方向优先级为最高的1000。 光这个还不够,我们还要确保内建大小的边缘跟随内建大小一起变化,从而保证我们的内建大小可以起作用,说白了,就是要求contentView中的子控件建立与superView的约束,我们先建立第一个UILabel(姓名、电话)与superview top 的间距约束,然后依次往下建立控件之间推荐间距的约束,左边同列控件建立左部对齐约束,右边同行内容的建立顶部对齐约束,垂直方向的间距约束,最底部的”预约结果Label”建立与superview bottom的间距约束。
特别提醒:与contentView的四边间距约束很重要,有了4个与contentView的边缘约束,才能保证contentView的大小跟随其subviews变化。
上面这些就是建立合理的约束条件,这里随便提醒一下,UILabel在IB中布局的大小如果跟内容计算出来的不一致就会有警告,比如UILabel长度为200,目前的内容为2个14号字的长度,那UILabel就会有警告,对于这种警告,你需要忍住你的强迫症。
二、计算行高
这一步在iOS7与iOS8上面就有所不同了,我们先来看最新的iOS8。
1、iOS8:
从iOS8开始引入了UITableViewCell的高度的自适应功能,在iOS8之前实现很麻烦的功能,iOS8以后就不需要自己动手去做了,稍后我们会看一下iOS7下面如何做。
//打开tableview的高度估算功能
_iTableView.rowHeight = UITableViewAutomaticDimension;
_iTableView.estimatedRowHeight = 70.0;
estimatedRowHeight必须设置为大于0,为了画面的过度顺畅,保证UITableview的高度变化不至于导致UITableview的大范围滚动而影响了用户视觉体验,我们用一个预估的平均值,这也就是所谓的预估值。estimatedRowHeight并不是最终的行高,当一个Cell需要显示的时候,会精确计算实际的行高,contentView的宽度就是UITableview的宽度减去Section Index,accessoryView等的宽度,contentView的高度则会自动根据其子视图的约束关系计算,当此精确值被计算出过后,estimatedRowHeight、tableview的contentSize,bounds,这些都会跟随更新。
estimatedRowHeight每一行都会使用此值用于该行的预估值,进而初步预估UITableView的contentSize。如果UITableView每行内容变化很大,行高差别很大,那我们可以使用以下方法为每一行设置各不相同的预估值。也就是说有通用值,也有个性值。
- (void)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
OK,预估值只是一个插曲,我们来看精确值,在iOS8过后,我们只需简单的激活预估值,并在heightForRowAtIndexPath中返回UITableViewAutomaticDimension即可。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (IS_IOS8_OR_ABOVE) {
return UITableViewAutomaticDimension;
}
}
2、iOS7:
UITableViewCell的contentView的高度自适应是iOS8中加入的,iOS7就只能自己计算了,所以我们来看一下iOS7下如何处理的。
//NIB注册,获取自定义UITableView实例的方式有很多种,这里随便用一种
UINib *cellNib = [UINib nibWithNibName:@"AppointmentedInfoCell" bundle:nil];
[self.tableView registerNib:cellNib forCellReuseIdentifier:@"AppointmentedInfoCell"];
//先创建一个基本的Cell实例,我们后面cellForRowAtIndexPath 和 heightForRowAtIndexPath 都需要用,由于UITableView的加载过程是先计算出所有的行高,再对每行进行渲染的,即 heightForRowAtIndexPath是先调用的,所以这里的baseCell就是一个离屏控件,用于辅助计算高度的,而后面也可以直接使用其用于每行内容的更新,每种类型的Cell只需要一个即可,这样我们的离屏内存并不会浪费。
_baseCell = [cellNib instantiateWithOwner:nil options:nil][0];
//更新Cell内容
- (void)configureCell:(AppointmentedInfoCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
//更新contentView的子控件
}
//
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
AppointmentedInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AppointmentedInfoCell" forIndexPath:indexPath];
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
//
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self configureCell:_baseCell atIndexPath:indexPath];
[_baseCell layoutSubviews];
CGFloat height = [_baseCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height + 1;//由于分割线,所以contentView的高度要小于row 一个像素。
}
写本文时,为了能够提供一个全面的解决方案不至于太局限,以免误导读者,我翻阅了很多文章,基本思想都是以上这样,区别在于细节的处理上,比如UItableViewCell的实例化就有很多种方式比如:
static NSString *CellIdentifier = @"AppointmentedInfoCell";
AppointmentedInfoCell *cell = (AppointmentedInfoCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *cellTeam = [[NSBundle mainBundle] loadNibNamed:CellIdentifier
owner:self
options:nil];
cell = [cellTeam objectAtIndex:0];
}
也有的网友巧妙的使用了dispatch_once:
static AppointmentedInfoCell *baseCell;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
baseCell = [[AppointmentedInfoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyTableViewCellIdentifier];
});
OK,到这里,我们基本上就掌握了AutoLayout的UITableViewCell的动态高度的处理,关键点就是systemLayoutSizeFittingSize的使用,希望本篇能够帮助你更好的理解AutoLayout以及UITableView在iOS8新增的布局处理。
参考:
本文主要用于一个知识的归纳总结,过程中可能会引用到其它地方的文字或代码,如有侵权请及时联系我,在此对写作过程中参考了的文章作者表示感谢!
- http://blog.jldagon.me/blog/2013/12/07/auto-layout-and-uitableview-cells/
- 对应demo
- http://useyourloaf.com/blog/2014/02/14/table-view-cells-with-varying-row-heights.html
- 对应demo
- http://www.raizlabs.com/dev/2014/02/leveraging-auto-layout-for-dynamic-cell-heights/
- 对应demo
- http://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights
- 中文翻译版
- PureLayout
- http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout
- https://github.com/Alex311/TableCellWithAutoLayout
- http://www.ifun.cc/blog/2014/02/21/dong-tai-ji-suan-uitableviewcellgao-du-xiang-jie/
- http://blog.amyworrall.com/post/66085151655/using-auto-layout-to-calculate-table-cell-height
- http://www.macspotsblog.com/dynamic-uitableview-cell-heights-programmatically/
- http://www.cnblogs.com/gatsbywang/p/4216706.html
AutoLayout深入浅出五[UITableView动态高度]的更多相关文章
- AutoLayout处理UITableView动态高度
我们经常会遇到UITableViewCell的高度要跟随内容而调整,在未引入AutoLayout之前,我们使用以下方法计算Label高度,然后heightForRowAtIndexPath中返回计算的 ...
- 使用Autolayout xib实现动态高度的TableViewCell
http://my.oschina.net/u/2360693/blog/481236?p={{totalPage}} 创建Xib文件 首先将Cell做好布局,调整到满意的位置和宽度,然后开始做Aut ...
- iOS开发——使用Autolayout生成动态高度的TableViewCell单元格
步骤一.TableViewCell中使用Autolayout 要点:Cell的高度必须在Constraints中指明,但不能定死,需要让内部由内容决定高度的View决定动态高度. 如UILabel设置 ...
- swift 版本 UItableViewCell的动态高度补足
用swift的朋友们很多都是从ios8开发了, 其中针对table cell高度自动计算的 UITableViewAutomaticDimension 异常好用,但好像只对uilabel对象有效 ...
- soui中,列表控件动态高度的使用注意
1.listview的模板template中,需要增加defHeight属性,即默认高度,同时,不能出现itemHeight属性,否则动态高度会失效 2.数据适配器中,重写getViewDesired ...
- UITableView动态存放、重用机制
一.UITableView动态存放 #import "ViewController.h"@interface ViewController ()<UITableViewDel ...
- Golang 网络爬虫框架gocolly/colly 五 获取动态数据
Golang 网络爬虫框架gocolly/colly 五 获取动态数据 gcocolly+goquery可以非常好地抓取HTML页面中的数据,但碰到页面是由Javascript动态生成时,用goque ...
- css中height 100vh的应用场景,动态高度百分比布局,浏览器视区大小单位
css中height 100vh的应用场景,动态高度百分比布局,浏览器视区大小单位 height:100vh 一些只能vw, vh才能完成的应用场景: 1. 场景之:元素的尺寸限制 vw vh 主要是 ...
- 精简计算UITableView文本高度
精简计算UITableView文本高度 本人视频教程系类 iOS中CALayer的使用 最终效果: 核心源码(计算文本高度的类) NSString+StringHeight.h 与 NSStrin ...
随机推荐
- jsp页面利用JS做即时时间显示
想给系统多些点缀,顺便把知识点温习一遍,不过讲真,这些都是网上扒的!我的水平现在也就这样 <%@page import="javax.xml.crypto.Data"%> ...
- 高通 打开 wifi 驱动 log
在WCNSS_qcom_cfg.ini文件中添加 vosTraceEnableSME=255 vosTraceEnableWDI=255 vosTraceEnableWDA=255 vosTraceE ...
- SQL数据查询之——嵌套查询
一.概念描述 在SQL语言中,一个 SELECT-FROM-WHERE 语句称为一个查询块.将一个查询块嵌套在另一个查询块的 WHERE 子句或 HAVING 短语的条件中的查询称为 嵌套查询.例如: ...
- Solr系列二:solr-部署详解(solr两种部署模式介绍、独立服务器模式详解、SolrCloud分布式集群模式详解)
一.solr两种部署模式介绍 Standalone Server 独立服务器模式:适用于数据规模不大的场景 SolrCloud 分布式集群模式:适用于数据规模大,高可靠.高可用.高并发的场景 二.独 ...
- Mybatis系列(二):优化MyBatis配置文件中的配置和解决字段名与实体类属性名不相同的冲突
原文链接:http://www.cnblogs.com/xdp-gacl/p/4264301.html http://www.cnblogs.com/xdp-gacl/p/4264425.ht ...
- windows下docker使用及注意事项
1.windows container模式下,pull镜像会失败 no matching manifest for windows/amd64 in the manifest list entries ...
- Kilo 版 Keystone 数据库结构
在安装完keystone并利用keystone-manage命令同步数据库后,mysql(我使用的存储后端)中新加了如下表: +------------------------+ | Tables_i ...
- Quorumpeps 群体感应数据库简介
群体感应的定义: 细菌能自发产生.释放一些特定的信号分子,并能感知其浓度变化,调节微生物的群体行为, 这一调控系统称为群体感应.细菌群体感应参与包括人类.动植物病原菌致病力在内的多种生物学功能的调节. ...
- [lua, mysql] 将多条记录数据组合成一条sql插入语句(for mysql)
-- 演示将多条记录数据组合成一条sql插入语句(for mysql) function getTpl0(tname) -- 获取表各个字段 local t = { tpl_pack = {" ...
- TensoFlow的tf.reshape()
tf.reshape(tensor,shape,name=None) 函数的作用是将tensor变换为参数shape形式,其中的shape为一个列表形式,特殊的是列表可以实现逆序的遍历,即list(- ...