Trying to get a "sticky header" that will orthogonally scroll with the section, but only partially, leaving the trailing end exposed until scrolled backwards.
Headers don't work as they don't scroll, so I've decided to just make it work as another cell in the section.
Demo Code: https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views (Download the project)
Open: Modern Collection Views > Compositional Layout > Advanced layouts View Controllers > OrthogonalScrollBehaviorViewController.swift
Replace
func createLayout() -> UICollectionViewLayout {
...
}
With
func createLayout() -> UICollectionViewLayout {
let config = UICollectionViewCompositionalLayoutConfiguration()
config.interSectionSpacing = 20
let layout = UICollectionViewCompositionalLayout(sectionProvider: {
(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
guard let sectionKind = SectionKind(rawValue: sectionIndex) else { fatalError("unknown section kind") }
let leadingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(0.5)))
let orthogonallyScrolls = sectionKind.orthogonalScrollingBehavior() != .none
let containerGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.4)),
subitems: [leadingItem])
let section = NSCollectionLayoutSection(group: containerGroup)
section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior()
section.visibleItemsInvalidationHandler = { (items, offset, env) in
let buffer: CGFloat = 50
for item in items {
if item.indexPath.item == 0 {
item.zIndex = 25
let w = item.frame.width
if offset.x >= (w - buffer) {
item.transform = CGAffineTransform(translationX: offset.x - (w - buffer), y: 0)
} else {
item.transform = .identity
}
} else {
item.zIndex = -item.indexPath.item
}
}
}
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44)),
elementKind: OrthogonalScrollBehaviorViewController.headerElementKind,
alignment: .top)
section.boundarySupplementaryItems = [sectionHeader]
return section
}, configuration: config)
return layout
}
As you can see it works perfectly RIGHT UP to where the cell is now "offscreen" according to its bounds, even though it is still clearly visible on screen.
I have also tried using a custom UICollectionViewCompositionalLayout that makes sure the IndexPath has an attribute in layoutAttributesForElements(in rect: CGRect), exactly the same results: As soon as the 'bounds' are offscreen the cell is removed even if the frame is very clearly still on screen.
Additionally, the line
item.transform = CGAffineTransform(translationX: offset.x - (w - buffer), y: 0)
can be anything that is functionally equivalent (the only other thing I've tried is moving the center) but the results are the same.