1

We tried to roll the UICollectionView like a list over the map. This is not to be confused with bottom sheet which snaps to a point (like low, mid and high). The collectionview's flowlayout property has sectionHeadersPinToVisibleBounds enabled. I have attached the sample project for your reference. Is there any way the header view can move to the top of the collectionview as the user scrolls?

Here is the sample project

Essential changes need for me to enter that state:

let layout = UICollectionViewFlowLayout()
layout.sectionHeadersPinToVisibleBounds = true
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0

let collectionView = UICollectionView(frame: .zero,
                                              collectionViewLayout: layout)


override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()

  collectionView.contentInset = UIEdgeInsets(top: drawerHeight, left: 0, bottom: 0, right: 0)
}

Here is a screenshot of what you would see: enter image description here

Red colored view is the header view which is pinned. Do I need a custom layout to update its position as the user scrolls?

Siddharthan Asokan
  • 4,321
  • 11
  • 44
  • 80

2 Answers2

1

I wrote a custom StickyHeaderLayout inspired from this post. Here is the fix to my bug:

class StickyHeaderLayout: UICollectionViewFlowLayout {

    override init() {
        super.init()
        configureLayout()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configureLayout()
    }

    private func configureLayout() {
        self.sectionFootersPinToVisibleBounds = true
        self.sectionHeadersPinToVisibleBounds = true
        minimumLineSpacing = 0
        minimumInteritemSpacing = 0
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }

        for attribute in attributes {
            adjustAttributesIfNeeded(attribute)
        }
        return attributes
    }

    override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let attributes = super.layoutAttributesForSupplementaryView(ofKind: elementKind, at: indexPath) else { return nil }
        adjustAttributesIfNeeded(attributes)
        return attributes
    }

    func adjustAttributesIfNeeded(_ attributes: UICollectionViewLayoutAttributes) {
        switch attributes.representedElementKind {
        case UICollectionView.elementKindSectionHeader?:
            adjustHeaderAttributesIfNeeded(attributes)
        default:
            break
        }
    }

    private func adjustHeaderAttributesIfNeeded(_ attributes: UICollectionViewLayoutAttributes) {
        guard let collectionView = collectionView else { return }
        guard attributes.indexPath.section == 0 else { return }

        if collectionView.contentOffset.y <= 0 {
            attributes.frame.origin.y = 0
        } else {
            attributes.frame.origin.y = collectionView.contentOffset.y
        }
    }
}
Siddharthan Asokan
  • 4,321
  • 11
  • 44
  • 80
0

one section is work, but if you have great than one section, it will cause some prolems

WuShuMin
  • 1
  • 2