将单元格高度固定到 UICollectionView 高度

Pin cell height to UICollectionView height

我有一个水平 UICollectionView,它包含在垂直 UICollectionViewUICollectionView header 中。垂直 UICollectionView 的 header 具有动态高度,该高度会因用户交互而变化(向下拖动外部 UICollectionView 会展开它,向上滚动会折叠它)。水平 UICollectionView 受限于 header 的大小,并且当 header 的高度减小时,高度也会缩小。发生这种情况时,我收到此错误

the behavior of the UICollectionViewFlowLayout is not defined because: 2018-07-19 13:42:09.959 IDAGIO[81891:2239798] the item height must be less than the height of the UICollectionView minus the section insets top and bottom values, minus the content insets top and bottom values. 2018-07-19 13:42:09.959 IDAGIO[81891:2239798] Please check the values return by the delegate.

因为 UICollectionView 的高度缩小了,但它的单元格保持原来的大小,因此高于允许的高度(我只在向上滚动时得到这个错误,所以它与任何插图无关,高度很好,只是在那个时间点不完全匹配。

我已经尝试在每次收到垂直 collection 视图的滚动事件时调用 collectionView.collectionViewLayout.invalidateLayout(),然后 return collection 视图大小 [=31] =] func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 但是布局更新总是有点太晚了,所以我仍然得到上面的错误。它看起来也很奇怪,因为单元格高度在视觉上也有点落后。

我的问题:有没有办法动态响应水平方向的高度变化 UICollectionView 以便单元格高度相应地及时更新? (也许设置一些高度约束自动保持单元格高度等于 collection 视图高度?)

我知道动态调整单元格大小,但这通常意味着将单元格限制为其内容的大小。我想要实现的是始终将单元格(及其内容)的高度限制为 collection 视图高度。

在滚动事件上重新加载数据是一种选择,但它绝对不是最好的选择,因为一次又一次地重新加载数据涉及所有计算。通过子类化 UICollectionViewLayout 并在 prepare() 上进行所有计算,您可能会找到更好的解决方案。然后您可以使用委托将滚动偏移量传递给您的自定义布局并相应地调整单元格大小。

我自己找到了一个简单的解决方案,根本不需要传递或存储滚动偏移量:
只需继承 UICollectionViewFlowLayout,覆盖 shouldInvalidateLayout 并将 itemSize 设置为新边界的大小。

override func shouldInvalidateLayout(forBoundsChange: CGRect) -> Bool {
    if !forBoundsChange.size.equalTo(collectionView!.bounds.size) {
        itemSize = forBoundsChange.size
        return true
    }
    return false
}

在Swift4.2

override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    if !newBounds.size.equalTo(collectionView!.bounds.size) {
        itemSize = newBounds.size
        return true
    }
    return false
}

UICollectionView 有一个派生自 UIScrollView 的 属性,称为 contentinsetadjustmentbehavior,如果您在collectionView 到 collectionView 边界的大小。

因为 scrollView 可以调整 insets 进而改变 collectionView.adjustedContentInset

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior

所以基本设置:

collectionView.contentInsetAdjustmentBehavior = .never