陳香君
陳香君

Reputation: 39

Swift: UICollectionView Header Disappear When Scrolling

this is my first post on StackOverflow, and I'm a newbie for Swift.

I implement android-like CollapsingToolbarLayout with UICollectionView, and I change the height of the header dynamically with custom CollectionViewFlowLayout. But When I scroll up, my header disappears, and I find it disappears at a certain height.

Do anyone have any idea or met the same problem ever?

I have tried to add headerReferenceSize or sectionHeaderPinToVisibleBound to my custom layout, but the former failed, and the latter fix height of the header.

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

        let layoutAttributes = super.layoutAttributesForElements(in: rect)

        layoutAttributes?.forEach({ (attribute) in

            if attribute.representedElementKind == UICollectionView.elementKindSectionHeader{

                guard let collectionView = collectionView else {return}

                let contentOffsetY = collectionView.contentOffset.y

                let width = collectionView.frame.width
                let height = attribute.frame.height - contentOffsetY
                let titleHeight = UIScreen.main.bounds.height/10

                let testHeight = attribute.frame.height - titleHeight

                if contentOffsetY > testHeight {
                    attribute.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: titleHeight)
                    headerReferenceSize = CGSize(width: width, height: titleHeight)                   
                } else {
                    attribute.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: height)
                }

            }

        })

        return layoutAttributes
    }

Youtube

Above is the link of question demo, sorry that I don't know how to paste the video on StackOverflow

Upvotes: 0

Views: 1716

Answers (1)

Dimitri
Dimitri

Reputation: 321

The reason why this happens is because layoutAttributesForElements is called with a different CGRect after a certain time when scrolling up.

I had the same effect and could eliminate it by setting sectionHeadersPinToVisibleBounds = true in viewDidLoad()

    if let layout = collectionViewLayout as? UICollectionViewFlowLayout {
        layout.sectionInset = .init(top: kPadding, left: kPadding, bottom: kPadding, right: kPadding)
        layout.minimumLineSpacing = 10
        layout.headerReferenceSize = .init(width: self.view.frame.width, height: ProfileViewHeader.kHeight)
        layout.sectionHeadersPinToVisibleBounds = true
    }

But layoutAttributesForElements(in:) will not be called for every move of the scroll view anymore. If you just need to set the layout for the header, better use layoutAttributesForElements(ofKind:at:) like so:

override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {

    if let attributes =  super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) {
        if attributes.representedElementKind == UICollectionView.elementKindSectionHeader {
            guard let collectionView = collectionView else { return attributes }
            let contentOffsetY = collectionView.contentOffset.y
            let width = collectionView.frame.width
            let newHeight = attributes.frame.height - contentOffsetY
            let height = newHeight > 64 ? newHeight: 64
            attributes.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: height)

            if let headerView = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader, at: attributes.indexPath) as? ProfileViewHeader {
                headerView.blur(alpha: (contentOffsetY > 0) ? 0.0 : abs(contentOffsetY * 2) / 100)
            }
        }
        return attributes
    }
    return nil
}

Upvotes: 1

Related Questions