Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DecorationView gets resized when AutoSizing cells are enabled in UICollectionViewFlowLayout

Environment:
UICollectionView that looks like UITableView
Custom UICollectionViewFlowLayout subclass to define the frame of the DecorationView
Self-Sizing cells enabled

Expected behavior:
A DecorationView that should be placed as a background for every section of the UICollectionView

Expected Behavior

Observed Behavior:
The DecorationView collapses to an arbitrary size:

Observed Behavior

Seems that UICollectionView tries to calculate an automatic size for the DecorationView. If I disable Self-Sizing cells, the decoration view is being placed exactly at the expected place.

Is there any way to disable Self-Sizing for DecorationView ?

In my UICollectionViewFlowLayout subclass I simply take the first and last cells in the section and stretch the background to fill the space underneath them. The problem is that UICollectionView does not respect the size calculated there:

override func layoutAttributesForDecorationView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    guard let collectionView = collectionView else {
      return nil
    }

    let section = indexPath.section
    let attrs = UICollectionViewLayoutAttributes(forDecorationViewOfKind: backgroundViewClass.reuseIdentifier(),
                                                 with: indexPath)
    let numberOfItems = collectionView.numberOfItems(inSection: section)
    let lastIndex = numberOfItems - 1

    guard let firstItemAttributes = layoutAttributesForItem(at: IndexPath(indexes: [section, 0])),
      let lastItemAttributes = layoutAttributesForItem(at: IndexPath(indexes: [section, lastIndex])) else {
        return nil
    }

    let startFrame = firstItemAttributes.frame
    let endFrame = lastItemAttributes.frame

    let origin = startFrame.origin
    let size = CGSize(width: startFrame.width,
                      height: -startFrame.minY + endFrame.maxY)

    let frame = CGRect(origin: origin, size: size)
    attrs.frame = frame
    attrs.zIndex = -1

    return attrs
}
like image 944
Richard Topchii Avatar asked Feb 27 '18 09:02

Richard Topchii


1 Answers

It's possible that the frames of your decoration views are not being updated (i.e. invalidated) after the frames of your cells have been self-sized. The result is that the width of each decoration view remains at its default size.

Try implementing this function, which should invalidate the layout of the decoration view for each section every time the layout of an item in that section is invalidated:

override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
    let invalidatedSections = context.invalidatedItemIndexPaths?.map { $0.section } ?? []
    let decorationIndexPaths = invalidatedSections.map { IndexPath(item: 0, section: $0) }
    context.invalidateDecorationElements(ofKind: backgroundViewClass.reuseIdentifier(), at: decorationIndexPaths)
    super.invalidateLayout(with: context)
}
like image 122
jamesk Avatar answered Oct 21 '22 19:10

jamesk