iOS8之后苹果为UITableView引入SelfSizing Cell,配合constraints的使用省去了动态计算Cell高度麻烦,而且有时还存在着计算不准确的尴尬。要想利用这个特性,Cell布局时必须要使用AutoLayout,无论是使用Interface Builder方式还是使用Coding方式,前提是必须能理解AutoLayout的布局思路和熟练使用Constraints。
TableView Cell能自己计算高度的原理其实是利用了AutoLayout中FittingSizing Control的原理, (可以参照我的另一篇文章《iOS开发之利用Autolayout做一个能WrapContent的自定义控件》)
关于FittingSizing 布局的官方相关的描述如下:
Intrinsic Content Size Versus Fitting Size
The intrinsic content size acts as an input to Auto Layout. When a view has an intrinsic content size, the system generates constraints to represent that size and the constraints are used to calculate the layout.The fitting size, on the other hand, is an output from the Auto Layout engine.It is the size calculated for a view based on the view’s constraints. If the view lays out its subviews using Auto Layout,then the system may be able to calculate a fitting size for the view based on its content.The stack view is a good example. Barring any other constraints, the system calculates the stack view’s size based on its content and attributes. In many ways, the stack view acts as if it had an intrinsic content size: You can create a valid layout using only a single vertical and a single horizontal constraint to define its position. But its size is calculated by Auto Layout—it is not an input into Auto Layout. Setting the stack view’s CHCR priorities has no effect, because the stack view does not have an intrinsic content size.If you need to adjust the stack view’s fitting size relative to items outside the stack view, either create explicit constraints to capture those relationships or modify the CHCR priorities of the stack’s contents relative to the items outside the stack.
大体理解起来就是FittingSizing视图的大小是基于施加于视图的上的约束计算而来,如果这个视图是个容器视图,则它所包含的子视图的填充大小就会影响这个视图的布局大小,比如这里要讲的TableView Cell。假设Cell上放置了一个Label,这里单元格的高度其实就是Label显示文本的高度,利用Autolayout,Label本身是可以知道有多大,但是Cell并不知道,如何关联呢?It is the size calculated for a view based on the view’s constraints.
就是这样的,我们让Cell的top equals to the top of Label AND bottom equals to the bottom of Label,可以想象Cell是在垂直方向被Label撑开了。
示例代码如下:
@implementation SelfSizingCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
if(self=[super initWithStyle:style reuseIdentifier:reuseIdentifier]){
_contentLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_contentLabel.numberOfLines = 0;
[self.contentView addSubview:_contentLabel];
_contentLabel.translatesAutoresizingMaskIntoConstraints = NO;
_contentLabel.preferredMaxLayoutWidth = CGRectGetWidth([UIScreen mainScreen].bounds)-30;
[_contentLabel setContentHuggingPriority:751 forAxis:0];
[_contentLabel setContentHuggingPriority:751 forAxis:1];
[_contentLabel setContentCompressionResistancePriority:250 forAxis:0];
[_contentLabel setContentCompressionResistancePriority:250 forAxis:1];
NSArray *_constraints = @[[NSLayoutConstraint constraintWithItem:_contentLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeLeft
multiplier:1
constant:15],
[NSLayoutConstraint constraintWithItem:_contentLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:self.contentView
attribute:NSLayoutAttributeRight
multiplier:1
constant:-15],
[NSLayoutConstraint constraintWithItem:_contentLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeTop
multiplier:1
constant:10],
[NSLayoutConstraint constraintWithItem:_contentLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeBottom
multiplier:1
constant:-10]];
[NSLayoutConstraint activateConstraints:_constraints];
}
return self;
}
@end
我们做Cell布局时一般将自定义控件放在ContentView上,所以施加的约束也是相对于ContentView,这个一定不能错!ContentView和Cell之间本身也是存在约束的,不需要我们考虑的。至于使用Masonry的代码会更简单、简洁,这里不贴代码了,而使用Interface builder添加约束的,不是很喜欢也不多做解释。
到这里几乎完成了这项工作的80%了,剩下的就是UITableView的属性配置了,即如下代码
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
estimatedRowHeight:默认值UITableViewAutomaticDimension。设置一个非负的值可以提高TableView的加载效率,提升体验性,而设置值为零则禁掉了预估高度的功能。如果要使用SelfSizing Cell是需要设置estimatedRowHeight属性的。
rowHeight:默认值UITableViewAutomaticDimension。如果是Coding的话,其实是不需要显示设置这个属性,只有用Interface Builder创建的Cell需要显示设置tableView.rowHeight = UITableViewAutomaticDimension
。
详细的描述可以参照官方文档,入口如下
rowHeight
estimatedRowHeight
至此,已经可以实现SelfSizing Cell的所有步骤,就是这么简单,如果你已经是使用AutoLayout的高手,有啥理由不使用SelfSizing Cell呢。 在这里还是推荐大家尽量多使用AutoLayout,毕竟这项技术还是挺不错的,用多了就会喜欢上这种布局的思路~~