I actually got it working. This code works XCode 12 Beta 2 / Target iOS14 / Swift 5.1.
In this piece of code, pay special attention to spacing, insets and orthogonalScrollingBehavior !!!
fileprivate func configureCollectionViewLayout() {
func createLayout() -> UICollectionViewLayout {
self.collectionView.canCancelContentTouches = false
let layout = UICollectionViewCompositionalLayout {
(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
guard let sectionKind = Section(rawValue: sectionIndex) else { return nil }
let spacing = CGFloat(10)
switch sectionKind {
case .someOtherSection:
// configure the layout for this section
case .yetAnotherSection:
// configure the layout for this section
case .sectionWithOrthogonalScrollingBehaviour:
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(1.0*9.0/16.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
group.interItemSpacing = .fixed(0) // should (probably) be 0 otherwise there is an offset when scrollingToIndex
section.interGroupSpacing = 0 // should (probably) be 0 otherwise there is an offset when scrollingToIndex
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0) // leading and trailing should (certainly) be 0 otherwise the pages are offsetted in relation to the view
section.orthogonalScrollingBehavior = .continuous // should be continous, otherwise it won't work
return section
}
}
return layout
}
collectionView.collectionViewLayout = createLayout()
}
To scroll programatically, I implemented a segmented control. When user taps on a segment, the orthogonally scrolling section scrolls automatically to the cell corresponding with the selected segment.
@IBAction func segmentedControlValueChanged(_ sender: UISegmentedControl) {
// determine which Item belongs to the selected segment. In my case Items in a Section are represented by an enum called 'Item'
let dataSourceIndexPath = self.dataSource.indexPath(for: .itemCase)
let pressentationIndexPath = self.collectionView.presentationIndexPath(forDataSourceIndexPath: dataSourceIndexPath)! // This step is necessary, otherwise the next line does not have have wanted behaviour (I'm force unwrapping here to simplify. Don't do that in your final code)
self.collectionView.scrollToItem(at: pressentationIndexPath, at: .centeredHorizontally, animated: true) // make sure you use .centeredHorizontally
}
I'm not sure this is how Apple intended this to be used. It might brake again in next Xcode beta versions.
Sidenote: In case you were wondering why I need/want a segmented
control to scroll a collection view: It's because the content of the
cells in the orthogonally scrolling section need to consume the
horizontal pan gestures (the cells are all charts that you can move your finger
along and then display the chart value at the touched location).
Yes... I know this can also be done in other ways, but I wanted to use
the compositional layout because it fits together nicely with the
other sections, and I wanted to keep this sweet built in animation of
the orthogonalscrollbehavior (even though this is now partially lost because orthogonalScrollingBehavior needs to be .continues for it to work) and I just wanted to see if it can be done :)