小心使用cell的contentView
UITableViewCell是每个iOS开发者都必须掌握的。我们都知道,应当将子视图加入其contentView,而非cell本身。但为什么这样做,如果不这样做有什么后果,你知道吗?
最近在开发中遇到一个问题:使用contentView做insets效果时,Auto Layout产生冲突。
起因源于需要实现这种效果:
即内容与cell四边保持距离,可以用UIEdgeInsets
表示。根据以往经验,既然所有子视图都被加入contentView,那么使用它做insets再合适不过了。代码如下:
contentView.snp.makeConstraints { (make) in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 10.0, left: 5.0, bottom: 10.0, right: 5.0))
}
但运行后的效果却不是我们想要的:
查看console中出现错误信息,提示布局冲突(这里我使用self-sizing cell,即cell的高度由Auto Layout自动计算):
仔细观察,可以发现:UIView-Encapsulated-Layout-Height
这条约束与cell的总体高度(20.0 + label高度 + 20.0)冲突,系统选择打破底部约束。从字面意思来看,前者似乎也在定义cell高度。而且,经过Google,发现这条约束由系统设置,优先级为required
,无法降级。
网上给出的说法是将我们自己的约束降级,避免冲突。但这种做法不符合需求:UIView-Encapsulated-Layout-Height这条约束所定义的cell高度不是我们想要的。那么,到底是哪里出了问题呢?
经过反复测试,终于发现问题所在:只要不使用contentView做insets,就一切安好。那么,为什么不能呢?带着强烈的好奇心,我翻阅文档。文档对于contentView的解释是这样的:
要点如下:
- cell子视图必须加入contentView;
- 之所以必须加入contentView,是因为在编辑模式下,cell会操作contentView。如下图所示:
此外,还在大片的错误信息中发现了一段非常不起眼提示:
大意为:不要修改contentView属性translatesAutoresizingMaskIntoConstraints
,否则后果自负😂。
SnapKit会自动修改视图的这个属性(false),怪不得会触发这个警告。
结论
经过上面的思考,可以得出结论:
- 子视图都必须加入contentView
- 不要修改contentView的布局属性,frame也好,添加约束也好,都是禁止的。
可能有人会问,这样的话如何实现insets效果呢?答案很简单:创建一个containerView,将所有子视图放到里面,然后把它加入contentView,再做insets。例如: