I am trying to apply a kind of reverse-parallax effect on the first cell in my collectionview by overriding the layoutAttributesForElements(in rect:)
method on UICollectionViewCompositionalLayout
, calling the super implementation to obtain the default values then manipulating the frame of only the item at indexPath (0,0).
class ScrollingCardCollectionViewLayout: UICollectionViewCompositionalLayout {
override func shouldInvalidateLayout(forBoundsChange _: CGRect) -> Bool {
return true
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attrs = super.layoutAttributesForElements(in: rect)
guard let attrs = attrs, let collectionView = collectionView else { return attrs }
if let attr = attrs.first(where: { $0.representedElementCategory == .cell && $0.indexPath.section == 0 && $0.indexPath.item == 0 }) {
let offset = collectionView.contentOffset
let inset = collectionView.contentInset
let adjustedOffset = offset.y + inset.top
attr.frame.origin.y = adjustedOffset / 3 * 2
}
}
}
This works perfectly when scrolling slowly, however if you scroll quickly it the scrollview seems to lose track of its contentOffset
and jerks back 3-5 times. This occurs both when both panning to scroll or 'flicking' to allow it to animate / decelerate. It also starts working perfectly once you have scrolled to the bottom of the content at least once (i.e. once the layout has established the heights of all the index paths).
I am using a vertical UICollectionViewCompositionalLayout with fixed full-screen width and dynamically sized (height) cells.
Full sample project available here
I've also tried setting an affine transform translation on the layout attributes which yielded the same results. Is this an iOS bug or am I doing something I shouldn't be?