5

I was wondering for a while now, what is the current way (if any) to set contentOffset for a collection view using CompositionalLayout.
I get it that CompositionalLayout built different and each section have it's own embedded scroll view, that's why we have to use visibleItemsInvalidationHandler to get contentOffset instead of the good old scrollViewDidScroll(_ scrollView: UIScrollView) delegate. But I can't seems to find any api for setting contentOffset back to the collection view (section or the whole composition).

For more concrete example lets say we have a collection view and a scroll view and I want to sync vertical scroll between them:

CollectionView:

private lazy var collectionView: UICollectionView = {
        let collectionViewLayout = createCompositionalLayout()
        return UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
    }()

Layout:

    private func createCompositionalLayout() -> UICollectionViewLayout {
        let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in
            let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[sectionIndex]
            let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(self.view.height))
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
            let section = NSCollectionLayoutSection(group: group)
            
            return section
        }
        
        if let collectionHeader = createHeaderSupplementaryItem() {
            let layoutConfiguration = UICollectionViewCompositionalLayoutConfiguration()
            layoutConfiguration.boundarySupplementaryItems = [collectionHeader]
            layout.configuration = layoutConfiguration
        }

        return layout
    }

With flow layout I would use the UIScrollViewDelegate to get the desired behavior:

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard self.scrollView == scrollView else {
            return
        }

        collectionView.contentOffset.y = scrollView.contentOffset.y
    }

But on compositional layout that causes a mess.
So is it possible to achieve the same behavior on compositional?
b.t.w obviously I've simplified my code for the example sake, my collection is way more complex and won't be easy to convert to flow layout.

ItayAmza
  • 819
  • 9
  • 21

1 Answers1

1

I not found an official api so I just find inner ScrollView manually and scroll it

I make this UICollectionView extension

func getScrollViewFromCompositionLayout(section: Int) -> UIScrollView? {
    return subviews
        .compactMap { $0 as? UIScrollView }
        .first(where: { scrollView in
            guard let cell = scrollView.subviews.first as? UICollectionViewCell else {
                return false
            }

            return indexPath(for: cell)?.section == section
        })
}
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 24 '23 at 00:40
  • It's the closest solution I've found, I would really appreciate if you explain through your code and provide more information. – Didami Apr 28 '23 at 02:57