集合视图网格布局高度撑开
本文讨论集合视图 + UICollectionViewFlowLayout + AutoLayout 时, 集合视图高度撑开至其 contentView
高度的方法.
实现思路介绍
首先来看一个简单的代码, 如下所示:
private func setupCollectionView() {
_collectionView = UICollectionView(frame: .zero, collectionViewLayout: _flowLayout)
_topView.addSubview(_collectionView)
_collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.height.equalTo(0)
}
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
_collectionView.snp.updateConstraints {// 单独 update 集合视图的高度约束
$0.height.equalTo(250)
}
}
而这段代码所在的上下文是这样的:
-
视图控制器的视图中有一个
UIScrollView
. -
在
UIScrollView
中有一个_topView
和一个_bottomView
. -
将集合视图放入
_topView
中, 当集合视图内容布局完成后, 它将_topView
的高度按contentSize
中的高度撑开.
代码中显示的仅仅是一套最基本的撑开操作, 下面来看如何 在布局完成的时机将界面撑开.
实际代码
上面的代码没有解决在 "什么时候" 布局完成的问题, 其实有一个很取巧的做法, 无需观察 contentSize
的改变就可以达到目的, 如下所示:
_collectionView.performBatchUpdates({
print("内容更新: \(self._collectionView.contentSize)")
}, completion: { _ in
print("内容更新完成: \(self._collectionView.contentSize)")
self._collectionView.snp.updateConstraints {
$0.height.equalTo(self._collectionView.contentSize.height)
}
})
performBatchUpdates
用于对集合视图进行批量修改操作. 而这里没有执行任何修改内容的操作, 而是利用它提供的完成回调来查看首次布局完成后的 contentSize
.
而这段代码的添加位置是在 viewDidLoad
, 这样在完成布局后即可获取到 contentSize
的准确值了.
最终效果如下所示(为了便于演示, 故添加了动画效果):
UIFlowLayout + AutoLayout + Self-Sizing 时候的自动撑开
上面的代码仅仅针对固定的 ItemSize 的情况, 如果设置 estimatedItemSize
, 则需要额外进行处理.
当前的布局参数配置如下:
private func configFlowLayout() {
_flowLayout.minimumLineSpacing = 8.0
_flowLayout.minimumInteritemSpacing = 8.0
_flowLayout.sectionInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
_flowLayout.estimatedItemSize = CGSize(width: 1.0, height: 1.0)
}
新建集合视图 Cell, 内部仅含有一个 UILabel:
class MyCollectionCell: UICollectionViewCell {
private let _label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(_label)
_label.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
func setText(_ text: String?) {
_label.text = text
}
}
实践发现, 原来如果集合视图的初始高度约束设置过小(比如之前设置的 0, 或者 1, 是无法正确布局内部的), 故将集合视图的初始高度约束从:
_collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.height.equalTo(0)
}
修改为:
_collectionView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.height.equalTo(10)
}
这样就可以正确地布局内部内容, 且高度仍然可以更新正常, 如下所示(为了便于演示, 还是添加了动画过程):
如果觉得上面的 Cell 中 Label 的边距离 Cell 的太近, 可以直接设置 Label 的约束为如下:
_label.snp.makeConstraints {
$0.edges.equalToSuperview().inset(8)
}
这样的话, 效果就更好了: