如何调整 UICollectionView 的高度为 UICollectionView 内容大小的高度?
How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?
在本例中,我希望 UICollectionView(红色的)缩小到内容大小的高度 UICollectionViewCells(黄色的),因为有很多空 space。我尝试的是使用:
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return self.collection.contentSize
}
但return self.collection.contentSize
总是return(宽度,0)
由于这个原因,它缩小到高度值 30(我在 XIB 文件中设置的高度值,尽管我有 constaint >= 30)。
首先计算单元格的数量,然后将其乘以单元格的高度,然后return在此方法中计算高度
collectionView.frame = CGRectMake (x,y,w,collectionView.collectionViewLayout.collectionViewContentSize.height); //objective c
//[collectionView reloadData];
collectionView.frame = CGRect(x: 0, y: 0, width: width, height: collectionView.collectionViewLayout.collectionViewContentSize.height) // swift
我建议如下:
- Add a height constraint to your collection view.
- Set its priority to 999.
- Set its constant to any value that makes it reasonably visible on the storyboard.
- Change the bottom equal constraint of the collection view to greater or equal.
- Connect the height constraint to an outlet.
- Every time you reload the data on the collection view do the following:
您可能还想考虑将集合视图的 Inset 添加到内容大小。
代码示例:
CGFloat height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
heightConstraint.constant = height
self.view.setNeedsLayout() Or self.view.layoutIfNeeded()
说明:额外的,看懂了就不用看了。很明显!!
UI 将尝试反映所有约束,无论他们的优先级是什么。由于存在优先级较低的 (999) 的高度约束和大于或等于类型的底部约束。每当高度约束常量设置为小于父视图高度的值时,集合视图将等于给定高度,从而实现两个约束。
但是,当高度约束常量设置为大于父视图高度的值时,这两个约束都无法实现。因此,只会实现优先级较高的约束,即大于或等于底部约束。
以下只是经验猜测。因此,它实现了一个约束。但是,它还尝试使结果 UI 中的 error 对于其他未实现的较低优先级约束 尽可能低 .因此,集合视图的高度将等于父视图的大小。
您必须将高度限制设置为等于内容大小
HeightConstraint.constant = collection.contentSize.height
执行以下操作。
- 首先为
UICollectionView
设置高度限制
- 这里
calendarBaseViewHeight
是UICollectionView
身高可变
重新加载集合视图后调用函数
func resizeCollectionViewSize(){
calendarBaseViewHeight.constant = collectionView.contentSize.height
}
我最终将 UICollectionView
子类化并重写了一些方法,如下所示。
- 为
intrinsicContentSize
返回 self.collectionViewLayout.collectionViewContentSize
确保始终具有正确的尺寸
- 然后只要它可能改变就调用它(比如
reloadData
)
代码:
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return self.collectionViewLayout.collectionViewContentSize
}
但请注意,如果您显示大量数据,即使屏幕放不下,您也会失去 "cell re-using"。
1) 设置 CollectionView 的固定高度。
2) 创建此 CollectionView 高度常量的出口。
喜欢:
IBOutlet NSLayoutConstraint *constHeight;
3) 在您的 .m 文件中添加以下方法:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat height = collectionMenu.collectionViewLayout.collectionViewContentSize.height;
constHeight.constant = height;
}
在您的 UICollectionView
上设置您的约束条件,例如 Trailing
、Leading
和 Bottom
:
如果您更详细地查看我的高度限制,因为它纯粹是为了看故事板,所以我不会出错,所以我要 Remove at build time
。实际高度限制在我下面的代码中设置。
我的代码DrawerCollectionView
,设置为集合视图Custom Class
:
import UIKit
class DrawerCollectionView: UICollectionView {
override func didMoveToSuperview() {
super.didMoveToSuperview()
heightAnchor.constraint(equalToConstant: contentSize.height).isActive = true
}
}
在Swift5和Xcode10.2.1
我的 CollectionView 名称是 myCollectionView
修复 CollectionView 的高度
为您的 CollectionViewHeight 创建 Outlet
IBOutlet weak var myCollectionViewHeight: NSLayoutConstraint!
使用下面的代码
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
myCollectionViewHeight.constant = height
self.view.layoutIfNeeded()
}
基于文本内容的单元格动态宽度...
Dynamic cell width of UICollectionView depending on label width
为我工作
let heightRes = resCollectionView.collectionViewLayout.collectionViewContentSize.height
foodHeightConstrant.constant = height.advanced(by: 1 )
foodCollectionView.setNeedsLayout()
foodCollectionView.layoutIfNeeded()
采用了 d4Rk 的解决方案,这很棒,除了在我的情况下它会不断切断我的集合视图的底部(太短)。我发现这是因为内在内容大小有时为 0,这会影响计算。 IDK。我所知道的就是这个修复了它。
import UIKit
class SelfSizedCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
let s = self.collectionViewLayout.collectionViewContentSize
return CGSize(width: max(s.width, 1), height: max(s.height,1))
}
}
获取单元格的高度。像这样
let cellHeight = cell.frame.height
获取集合视图的来源
let cvOrigin = collectionView.frame.origin
获取集合视图的宽度
let cvWidth = collectionView.bounds.width
设置内容视图的框架
collection.frame = CGRect(x: cvOrigin.x, y: cvOrigin.y, width: cvWidth, height: cellHeight )
这对我来说似乎是最简单的解决方案。
class SelfSizingCollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
isScrollEnabled = false
}
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return contentSize
}
}
您可能不需要覆盖 reloadData
- 子类 UICollectionView 如下
- 删除高度限制
- 开启内部尺寸
-
class ContentSizedCollectionView: UICollectionView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
}
如果设置集合视图的高度限制。只需观察 viewDidLoad 中的 contentSize
变化并更新约束。
self.contentSizeObservation = collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, change in
guard let `self` = self else { return }
guard self.collectionView.contentSize != .zero else { return }
self.collectionViewHeightLayoutConstraint.constant = self.collectionView.contentSize.height
}
我有一个多行、多选的 UICollectionView
子类,其中单元格的高度固定且左对齐,从左到右流动。它嵌入在垂直滚动视图内的垂直堆栈视图中。请参阅标签“属性 类型”下方的 UI 组件。
为了让集合视图适合其 contentSize
的高度,这是我必须做的(请注意,这都在 UICollectionView
子类中):
给集合视图一个非零的优先级最小高度约束999
。将集合视图的大小自动调整为其内容高度在零高度下根本不起作用。
let minimumHeight = heightAnchor.constraint(greaterThanOrEqualToConstant: 1)
minimumHeight.priority = UILayoutPriority(999)
minimumHeight.isActive = true
将垂直轴的集合视图的内容拥抱优先级设置为 .required
。
setContentHuggingPriority(.required, for: .vertical)
调用 reloadData()
之后是以下调用:
invalidateIntrinsicContentSize()
setNeedsLayout()
layoutIfNeeded()
例如,我的子类中有一个setItems()
函数:
func setItems(_ items: [Item]) {
self.items = items
selectedIndices = []
reloadData()
invalidateIntrinsicContentSize()
setNeedsLayout()
layoutIfNeeded()
}
覆盖 contentSize
和 intrinsicContentSize
如下:
override var intrinsicContentSize: CGSize {
return contentSize
}
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
setNeedsLayout()
layoutIfNeeded()
}
}
正在将 UICollectionView 的高度调整为其内容大小的高度
SWIFT 5
final class MyViewController: UIViewController {
// it's important to declare layout as separate constant due to late update in viewDidLayoutSubviews()
private let layout = UICollectionViewFlowLayout()
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
setupCollectionViewConstraints()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateFlowLayout()
}
private func setupCollectionView() {
view.addSubview(collectionView)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")
collectionView.dataSource = self
}
private func setupCollectionViewConstraints() {
// your collectionView constraints setup
}
private func updateFlowLayout() {
let height = collectionView.collectionViewLayout.collectionViewContentSize.height
layout.itemSize = CGSize(width: view.frame.width, height: height)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = .zero
layout.minimumLineSpacing = .zero
layout.sectionInset = UIEdgeInsets.zero
}
}
extension MyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {...}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}
}
在本例中,我希望 UICollectionView(红色的)缩小到内容大小的高度 UICollectionViewCells(黄色的),因为有很多空 space。我尝试的是使用:
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
return self.collection.contentSize
}
但return self.collection.contentSize
总是return(宽度,0)
由于这个原因,它缩小到高度值 30(我在 XIB 文件中设置的高度值,尽管我有 constaint >= 30)。
首先计算单元格的数量,然后将其乘以单元格的高度,然后return在此方法中计算高度
collectionView.frame = CGRectMake (x,y,w,collectionView.collectionViewLayout.collectionViewContentSize.height); //objective c
//[collectionView reloadData];
collectionView.frame = CGRect(x: 0, y: 0, width: width, height: collectionView.collectionViewLayout.collectionViewContentSize.height) // swift
我建议如下:
- Add a height constraint to your collection view.
- Set its priority to 999.
- Set its constant to any value that makes it reasonably visible on the storyboard.
- Change the bottom equal constraint of the collection view to greater or equal.
- Connect the height constraint to an outlet.
- Every time you reload the data on the collection view do the following:
您可能还想考虑将集合视图的 Inset 添加到内容大小。
代码示例:
CGFloat height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
heightConstraint.constant = height
self.view.setNeedsLayout() Or self.view.layoutIfNeeded()
说明:额外的,看懂了就不用看了。很明显!!
UI 将尝试反映所有约束,无论他们的优先级是什么。由于存在优先级较低的 (999) 的高度约束和大于或等于类型的底部约束。每当高度约束常量设置为小于父视图高度的值时,集合视图将等于给定高度,从而实现两个约束。
但是,当高度约束常量设置为大于父视图高度的值时,这两个约束都无法实现。因此,只会实现优先级较高的约束,即大于或等于底部约束。
以下只是经验猜测。因此,它实现了一个约束。但是,它还尝试使结果 UI 中的 error 对于其他未实现的较低优先级约束 尽可能低 .因此,集合视图的高度将等于父视图的大小。
您必须将高度限制设置为等于内容大小
HeightConstraint.constant = collection.contentSize.height
执行以下操作。
- 首先为
UICollectionView
设置高度限制
- 这里
calendarBaseViewHeight
是UICollectionView
身高可变 重新加载集合视图后调用函数
func resizeCollectionViewSize(){ calendarBaseViewHeight.constant = collectionView.contentSize.height }
我最终将 UICollectionView
子类化并重写了一些方法,如下所示。
- 为
intrinsicContentSize
返回self.collectionViewLayout.collectionViewContentSize
确保始终具有正确的尺寸 - 然后只要它可能改变就调用它(比如
reloadData
)
代码:
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return self.collectionViewLayout.collectionViewContentSize
}
但请注意,如果您显示大量数据,即使屏幕放不下,您也会失去 "cell re-using"。
1) 设置 CollectionView 的固定高度。
2) 创建此 CollectionView 高度常量的出口。 喜欢:
IBOutlet NSLayoutConstraint *constHeight;
3) 在您的 .m 文件中添加以下方法:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CGFloat height = collectionMenu.collectionViewLayout.collectionViewContentSize.height;
constHeight.constant = height;
}
在您的 UICollectionView
上设置您的约束条件,例如 Trailing
、Leading
和 Bottom
:
如果您更详细地查看我的高度限制,因为它纯粹是为了看故事板,所以我不会出错,所以我要 Remove at build time
。实际高度限制在我下面的代码中设置。
我的代码DrawerCollectionView
,设置为集合视图Custom Class
:
import UIKit
class DrawerCollectionView: UICollectionView {
override func didMoveToSuperview() {
super.didMoveToSuperview()
heightAnchor.constraint(equalToConstant: contentSize.height).isActive = true
}
}
在Swift5和Xcode10.2.1
我的 CollectionView 名称是 myCollectionView
修复 CollectionView 的高度
为您的 CollectionViewHeight 创建 Outlet
IBOutlet weak var myCollectionViewHeight: NSLayoutConstraint!
使用下面的代码
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let height = myCollectionView.collectionViewLayout.collectionViewContentSize.height myCollectionViewHeight.constant = height self.view.layoutIfNeeded() }
基于文本内容的单元格动态宽度...
Dynamic cell width of UICollectionView depending on label width
为我工作
let heightRes = resCollectionView.collectionViewLayout.collectionViewContentSize.height
foodHeightConstrant.constant = height.advanced(by: 1 )
foodCollectionView.setNeedsLayout()
foodCollectionView.layoutIfNeeded()
采用了 d4Rk 的解决方案,这很棒,除了在我的情况下它会不断切断我的集合视图的底部(太短)。我发现这是因为内在内容大小有时为 0,这会影响计算。 IDK。我所知道的就是这个修复了它。
import UIKit
class SelfSizedCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
let s = self.collectionViewLayout.collectionViewContentSize
return CGSize(width: max(s.width, 1), height: max(s.height,1))
}
}
获取单元格的高度。像这样
let cellHeight = cell.frame.height
获取集合视图的来源
let cvOrigin = collectionView.frame.origin
获取集合视图的宽度
let cvWidth = collectionView.bounds.width
设置内容视图的框架
collection.frame = CGRect(x: cvOrigin.x, y: cvOrigin.y, width: cvWidth, height: cellHeight )
这对我来说似乎是最简单的解决方案。
class SelfSizingCollectionView: UICollectionView {
override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
super.init(frame: frame, collectionViewLayout: layout)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
isScrollEnabled = false
}
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
override var intrinsicContentSize: CGSize {
return contentSize
}
}
您可能不需要覆盖 reloadData
- 子类 UICollectionView 如下
- 删除高度限制
- 开启内部尺寸
-
class ContentSizedCollectionView: UICollectionView {
override var contentSize:CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
}
}
如果设置集合视图的高度限制。只需观察 viewDidLoad 中的 contentSize
变化并更新约束。
self.contentSizeObservation = collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, change in
guard let `self` = self else { return }
guard self.collectionView.contentSize != .zero else { return }
self.collectionViewHeightLayoutConstraint.constant = self.collectionView.contentSize.height
}
我有一个多行、多选的 UICollectionView
子类,其中单元格的高度固定且左对齐,从左到右流动。它嵌入在垂直滚动视图内的垂直堆栈视图中。请参阅标签“属性 类型”下方的 UI 组件。
为了让集合视图适合其 contentSize
的高度,这是我必须做的(请注意,这都在 UICollectionView
子类中):
给集合视图一个非零的优先级最小高度约束
999
。将集合视图的大小自动调整为其内容高度在零高度下根本不起作用。let minimumHeight = heightAnchor.constraint(greaterThanOrEqualToConstant: 1) minimumHeight.priority = UILayoutPriority(999) minimumHeight.isActive = true
将垂直轴的集合视图的内容拥抱优先级设置为
.required
。setContentHuggingPriority(.required, for: .vertical)
调用
reloadData()
之后是以下调用:invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded()
例如,我的子类中有一个
setItems()
函数:func setItems(_ items: [Item]) { self.items = items selectedIndices = [] reloadData() invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded() }
覆盖
contentSize
和intrinsicContentSize
如下:override var intrinsicContentSize: CGSize { return contentSize } override var contentSize: CGSize { didSet { invalidateIntrinsicContentSize() setNeedsLayout() layoutIfNeeded() } }
正在将 UICollectionView 的高度调整为其内容大小的高度
SWIFT 5
final class MyViewController: UIViewController {
// it's important to declare layout as separate constant due to late update in viewDidLayoutSubviews()
private let layout = UICollectionViewFlowLayout()
private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
override func viewDidLoad() {
super.viewDidLoad()
setupCollectionView()
setupCollectionViewConstraints()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
updateFlowLayout()
}
private func setupCollectionView() {
view.addSubview(collectionView)
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")
collectionView.dataSource = self
}
private func setupCollectionViewConstraints() {
// your collectionView constraints setup
}
private func updateFlowLayout() {
let height = collectionView.collectionViewLayout.collectionViewContentSize.height
layout.itemSize = CGSize(width: view.frame.width, height: height)
layout.scrollDirection = .horizontal
layout.minimumInteritemSpacing = .zero
layout.minimumLineSpacing = .zero
layout.sectionInset = UIEdgeInsets.zero
}
}
extension MyViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {...}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}
}