UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神

Posted

技术标签:

【中文标题】UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神【英文标题】:UICollectionView with autosizing cell (estimatedSize) and sectionHeadersPinToVisibleBounds goes mental 【发布时间】:2017-01-27 15:24:23 【问题描述】:

考虑以下情况。我有一个UICollectionView(在UICollectionViewController 内),它看起来与UITableView 几乎相同(我不使用UITalbeView 的原因是因为我在布局上有非数据视图,这是我不想要的管理和弄乱我的IndexPath)。 为了实现自动调整单元格大小,我设置了estimatedItemSize,如下所示:

layout.estimatedItemSize = CGSize(width: self.view.bounds.size.width, height: 72)

另外,在我的单元格中,我有布局属性:

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
    layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
    return layoutAttributes

因此,通过这样做,我得到了与 UITableView 一样的精确布局,并具有自动调整大小。而且效果很好。

现在,我正在尝试添加标题并将其固定在滚动到该部分的顶部,如下所示:

layout.sectionHeadersPinToVisibleBounds = false

但是布局进入了奇怪的状态,我到处都有小故障,单元格相互重叠,并且标题有时不粘。

更新:

view controller和cell的代码:

class ViewController: UICollectionViewController 

override func viewDidLoad() 
    super.viewDidLoad()
    let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
    layout.sectionHeadersPinToVisibleBounds = true
    layout.estimatedItemSize = CGSize(width: collectionView?.bounds.size.width ?? 0, height: 36) // enables dynamic height


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


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


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

    let cell =  collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
    cell.heightConstraint.constant = CGFloat(indexPath.row * 10 % 100) + 10 // Random constraint to make dynamic height work

    return cell


override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 
    return collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath)


class CustomCell : UICollectionViewCell 
let identifier = "CustomCell"

@IBOutlet weak var rectangle: UIView!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!

override func awakeFromNib() 
    translatesAutoresizingMaskIntoConstraints = false


override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
    layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
    return layoutAttributes

视频滞后详情:https://vimeo.com/203284395

【问题讨论】:

添加一些图片以供参考将有助于我们可视化您遇到的问题 能否附上截图? 我没有答案,但部分问题可能是您基于self.view.bounds.size.width 设置layout.estimatedItemSize。如果您使用自动布局,那么width 有效的地方是违反直觉的。你所做的基本上是递归的:你根据width 设置一些东西,然后导致视图被调整大小,这导致width 发生变化。尝试删除该循环依赖。 不,这是不对的。边界宽度与单元格布局无关。布局只影响单元格的大小和位置,而不影响集合视图本身的大小和位置。另外, self.view 是这里的集合视图的超级视图,所以根本不重要。它只是一个常数。 @PavelGatilov 添加您的 UI 图像和一些代码工作您所拥有的,以便我们可以准确估计需求并为您提供最佳答案,否则答案将是假设。 【参考方案1】:

WWDC 2017 更新:

我的同事在 WWDC 2017 上,他向一位 UIKit 工程师询问了这个问题。工程师确认这个问题是 Apple 已知的 bug,目前还没有修复。

【讨论】:

这个有什么新东西吗?【参考方案2】:

使用UICollectionViewDelegateFlowLayout 方法。

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
        //Calculate your Dynamic Size Here for Each Cell at SpecificIndexPath.
        //For Example You want your Cell Height to be dynamic with Respect to indexPath.row number
        let cellWidth = collectionView?.bounds.size.width
        //Now Simply return these CellWidth and Height, and you are Good to go.
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath)
        //You Can Pass TextField As Well as i have considered UITextView for now.
        let cellSize = self. calculateSize(cellTextView: cell.textView, cellText: yourArrayOfText[indexPath.row], withFixedWidth: cellWidth)
        return cellSize
    

并且不要通过直接更改 Constraint.constant 来更改 Cell Height。而不是这个简单地使用 Above Delegate 方法来改变高度。更改单元约束可能会导致此类问题。

使用下面的方法得到你想要的尺寸。

   func calculateSize(cellTextView: UITextView, cellText: String, withFixedWidth: CGFloat) -> CGSize 
            let textView = UITextView()
            textView.text = cellText
            textView.frame = cellTextView.frame
            textView.font = cellTextView.font
            textView.tag = cellTextView.tag
            let fixedWidth = withFixedWidth
            textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
            var newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
            return newSize
        

使用 Bellow 方法计算图像大小。

//Method to Calculate ImageSize.
func calculateImageSize(image: UIImage) -> CGSize 
        var newSize = image.size
        if newSize.width > (ChatView.maxWidth - 70) 
            newSize.width = ChatView.maxWidth - 70
            newSize.height = ChatView.maxWidth - 70
        
        if newSize.height < 60 
            newSize.height = 60
            newSize.width = 60
        
        return newSize
    

【讨论】:

好的。你不明白。约束只代表逻辑的一部分。它可以由任何动态的东西驱动,比如标签或图像中的文本数量等。 所以你想改变 Cell with Respect 的大小到里面的文字?? 不一定是文本,但是是的,背后的想法是根据内部内容的大小更改单元格大小。当我添加粘贴标题时,一切都出错了。 现在检查我的答案。我已经制定了一种方法来获取 TextView 相对于文本的大小。逻辑是创建一个空单元格并将 Cell 的 textView 沿文本和宽度传递给方法。现在您可以创建一个新的 textView 并将字体大小、字体类型分配给单元格的 textView 到这个新的 textView 并计算高度。现在只需将该高度和固定宽度返回给 UICollectionViewDelegateFlowLayout 方法。您现在将了解如何执行此操作。 我已经附上了计算图像大小的方法。

以上是关于UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionView 自定义布局动画故障,在 performBatchUpdates 上具有自我调整单元格大小

在 UICollectionView 中自动调整单元格(全部在代码中)

UICollectionView 具有自调整大小的单元格未正确计算 contentSize

设备旋转时如何自动调整 UICollectionView 的大小?

自动调整UICollectionView高度以调整其内容大小

UICollectionView 总是自动调整单元格大小。不使用委托返回的尺寸