约束 UICollectionView 高度以匹配内容的高度

Posted

技术标签:

【中文标题】约束 UICollectionView 高度以匹配内容的高度【英文标题】:Constrain UICollectionView height to match height of contents 【发布时间】:2018-09-23 06:30:31 【问题描述】:

我有一个使用 UICollectionViewFlowLayout 的 UICollectionView。滚动方向是垂直的,但我只有少量(可变)单元格,不需要或不想滚动。集合视图顶部、前导和尾随被限制为父视图。我正在尝试使集合视图高度适合其内容。

如果我使用这个,初始高度看起来是正确的:

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate 
    var collection: UICollectionView!
    var heightConstraint: NSLayoutConstraint!

    override func loadView() 
        super.loadView()
        self.collection = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        self.collection.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(self.collection)
        // ...
        self.collection.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
        self.collection.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor).isActive = true
        self.collection.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor).isActive = true

        // Create placeholder constraint
        self.heightConstraint = self.collection.heightAnchor.constraint(equalToConstant: 1)
        self.heightConstraint.isActive = true
    

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
        // Update placeholder constraint
        self.heightConstraint.constant = self.collection.contentSize.height
    

    // ...

但这不会在内容布局发生变化时更新集合视图的高度,例如在设备方向发生变化时。当内容布局发生变化时,有没有办法更新约束常量?或者一种使用动态约束而不是常量的方法?

Swift 4.2,Xcode 10.0,目标 ios 12。

【问题讨论】:

题外话:你不应该像这样覆盖loadView,你应该覆盖viewDidLoadloadView 旨在创建自定义视图。 【参考方案1】:

更新约束的更好位置是viewDidLayoutSubviews,在每次重新布局(方向更改、大小更改等)后都会调用它。此外,向集合视图布局询问内容大小更可靠:

override func viewDidLayoutSubviews() 
    super.viewDidLayoutSubviews()

    // Update placeholder constraint
    self.heightConstraint.constant = self.collection.collectionViewLayout.collectionViewContentSize.height

【讨论】:

【参考方案2】:

这里有一个解决方案:

lazy var collectionView: UICollectionView = 
    let collection = UICollectionView(frame:CGRect.zero, collectionViewLayout: collectionViewFlowLayout)
    collection.translatesAutoresizingMaskIntoConstraints = false
    collection.backgroundColor = .red
    collection.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "myCollectionCell")
    return collection
()

var defaultConstraints: [NSLayoutConstraint]?

var heightConstraint:[NSLayoutConstraint]?

let collectionViewFlowLayout: UICollectionViewFlowLayout = 
    let flow = UICollectionViewFlowLayout()
    flow.scrollDirection = .vertical
    flow.itemSize = CGSize(width: 120.0, height: 170.0)
    return flow
()

override func updateViewConstraints() 
super.updateViewConstraints()
    if defaultConstraints == nil 
        defaultConstraints = [
            collectionView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: self.collectionView.safeAreaLayoutGuide.bottomAnchor)
        ]
         heightConstraint = [collectionView.heightAnchor.constraint(equalToConstant: 100)]
heightConstraint?.first!.priority = .defaultHigh
        NSLayoutConstraint.activate(defaultConstraints! + heightConstraint!)
    



override func viewDidLoad() 
    super.viewDidLoad()
    view.addSubview(self.collectionView)
    collectionView.delegate = self
    collectionView.dataSource = self
    view.updateConstraintsIfNeeded()


override func viewDidLayoutSubviews() 
super.viewDidLayoutSubviews()
     heightConstraint?.first?.constant = collectionView.collectionViewLayout.collectionViewContentSize.height



func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    return 3


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCollectionCell", for: indexPath)
    cell.backgroundColor = .yellow
    return cell


func numberOfSections(in collectionView: UICollectionView) -> Int 
    return 1
  

【讨论】:

以上是关于约束 UICollectionView 高度以匹配内容的高度的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewCell 的动态高度以编程方式与约束?

UICollectionView 上的动画自动布局约束更改

使用布局约束调整 UICollectionView 的大小。节标题移动

更新接口构建器以匹配约束

StackView 内 UICollectionView 的动态高度

嵌套 UICollectionView 时设置 ScrollView 高度问题