仅通过自动布局确定 UICollectionView 单元格高度

Posted

技术标签:

【中文标题】仅通过自动布局确定 UICollectionView 单元格高度【英文标题】:Determine UICollectionView cell height only by autolayout 【发布时间】:2017-12-29 02:19:53 【问题描述】:

我需要一个在网格中显示单元格的集合视图。所以标准的流程布局对我来说很好。但是,我想知道每行显示多少个单元格,而单元格高度应该由我放在单元格上的自动布局约束来确定。这是我的单元格布局:

这很简单——一个图像视图和它下面的两个标签。现在图像视图有一个纵横比约束(1:1),这意味着只要单元格的宽度已知,自动布局规则就应该自动知道高度(有垂直约束通过: celltop-image-label1-label2-cellbottom)。

现在,由于我不知道任何其他好的方法来告诉集合视图每行显示 2 个项目,我已经覆盖了 UICollectionViewDelegateFlowLayout 方法:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    let availableWidth = collectionView.frame.width - padding
    let widthPerItem = availableWidth / itemsPerRow

    return CGSize(width: widthPerItem, height: widthPerItem)

如您所见,由于我不知道项目高度,因此我返回与宽度相同的值,希望稍后自动布局能够修复它。我还设置了estimatedItemSize,以便整个机制开始工作。

结果很奇怪 - 集合视图似乎没有考虑我返回那里的宽度,主要取决于标签长度:

我看到了一些其他答案,人们建议手动计算单元格的宽度大小,比如告诉“自己布局,然后测量自己,然后给我这个宽度的大小”,即使它仍然会运行自动布局规则在幕后,我想知道是否有办法做到这一点,而无需手动弄乱尺寸。

【问题讨论】:

【参考方案1】:

你可以在storyboard的Size inspector中很容易的找到你collectionView单元格的高度,如下图:

现在,只需从这里拾取这个高度,并将其传递给被覆盖的 UICollectionViewDelegateFlowLayout 方法:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    let availableWidth = collectionView.frame.width - padding
    let widthPerItem = availableWidth / itemsPerRow

    return CGSize(width: widthPerItem, height: **114**)

你会得到想要的输出。

【讨论】:

我希望一切都由自动布局处理,而且硬编码的高度将不起作用,因为不同屏幕尺寸的实际高度会有所不同。【参考方案2】:

我最终实现了将所有内容移动到自动布局的技巧。我完全删除了委托方法func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize,并在界面构建器中为我的单元格内容添加了宽度约束(将初始值设置为某个值,这并不重要)。然后,我在自定义单元类中为该约束创建了一个出口:

class MyCell: UICollectionViewCell 

    @IBOutlet weak var cellContentWidth: NSLayoutConstraint!

    func updateCellWidth(width: CGFloat) 
        cellContentWidth.constant = width
    

稍后,当创建单元格时,我根据每行所需的单元格数将宽度约束更新为预先计算的值:

private var cellWidth: CGFloat 
    let paddingSpace = itemSpacing * (itemsPerRow - 1) + sectionInsets.left + sectionInsets.right
    let availableWidth = collectionView.frame.width - paddingSpace
    return availableWidth / itemsPerRow


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 

    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell

    ...

    cell.updateCellWidth(width: cellWidth)

    return cell

这与启用自动布局单元格大小一起,将正确布局单元格。

【讨论】:

以上是关于仅通过自动布局确定 UICollectionView 单元格高度的主要内容,如果未能解决你的问题,请参考以下文章

在布局更改的同时重新加载/动画 UICollectionView

Swift:自动布局 - 文本列 - 删除尾随和前导警告

UILabel 自动布局适合

iOS:多行 uilabel 仅显示带有自动布局的一行

Swift Xcode 自动布局约束仅针对 iphone 6 plus 和 ipad

使用自动布局和约束更改位置