Search code examples
iosswiftxcodeuikit

Custom CollectionView header disappears during scroll (Custom UICollectionFlowLayout)


I am making a custom UICollectionViewFlowLayout and want my second and only second header to "pin" to the top during scroll, creating the floating header effect. However, in my current code, the header is pinned but disappears after a certain content offset but then will re-appear if you scroll back.

class StickyLayout: UICollectionViewFlowLayout {

    let kind = UICollectionView.elementKindSectionHeader

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect)

        layoutAttributes?.forEach({ (attributes) in

            if(attributes.representedElementKind == kind && attributes.indexPath.section == 1) {
                guard let collectionView = collectionView else { return }
                let contentOffset = collectionView.contentOffset.y
                var headerFrame = attributes.frame
                headerFrame.size.height = 90

                if(contentOffset > 300) {
                    headerFrame.origin.y = contentOffset
                    attributes.frame = headerFrame
                }
            }
        })
        return layoutAttributes
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
 }

Solution

  • Found the solution. The problem was that the header was no longer in the rect after a certain distance. So you have to add the header's attributes right before looping through them. Adding this right before my for loop seems to have fixed the problem.

    let stickyIndexPath = IndexPath(item: 0, section: 1)
    if let headerAttributes = layoutAttributesForSupplementaryView(ofKind: kind, at: stickyIndexPath) {
        if !layoutAttributes!.contains(headerAttributes) {
            layoutAttributes?.append(headerAttributes)
        }
    }