I'm experiencing a weird layout issue on iOS 14.3 with collection views using a UICollectionViewCompositionalLayout
combined in my case with the UICollectionViewDiffableDataSource
.
The issue is about the wrong position of the inner _UICollectionViewOrthogonalScrollerEmbeddedScrollView
when you have an orthogonal section preceded by an intrinsic height section.
Fortunately I'm able to reproduce the issue very easily. Consider having this data source:
private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
enum Section: Int, Hashable, CaseIterable {
case first = 0
case second = 1
}
For each section you create the following layout:
private extension Section {
var section: NSCollectionLayoutSection {
switch self {
case .first:
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(50))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section: NSCollectionLayoutSection = .init(group: group)
return section
case .second:
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(200), heightDimension: .absolute(200))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section: NSCollectionLayoutSection = .init(group: group)
section.orthogonalScrollingBehavior = .continuous
section.contentInsets = .init(top: 10, leading: 10, bottom: 10, trailing: 10)
section.interGroupSpacing = 10
return section
}
}
}
The thing breaking the layout is having into the .first
section both itemSize
and groupSize
with .estimated
height.
You can see the result below on iOS 14.3: at the first glance the layout is visually correct, but you immediately realize the fact that it's broken because the inner scroll view is in the wrong place. This implies that the horizontal scroll happens wrongly in the blue area.
Running exactly the same code up to iOS 14.2 you get the correct layout .
What do you think about this issue? Am I missing something or it could be a UIKit bug?
Thanks
We have header with estimated height and some sections with _UICollectionViewOrthogonalScrollerEmbeddedScrollView
were completely broken because of this regression. So here is a solution that worked in our case
public final class CollectionView: UICollectionView {
public override func layoutSubviews() {
super.layoutSubviews()
guard #available(iOS 14.3, *) else { return }
subviews.forEach { subview in
guard
let scrollView = subview as? UIScrollView,
let minY = scrollView.subviews.map(\.frame.origin.y).min(),
minY > scrollView.frame.minY
else { return }
scrollView.contentInset.top = -minY
scrollView.frame.origin.y = minY
}
}
}
For me this problem is reproducible in iOS 14.3
and 14.4
. And now is fixed in iOS 14.5 beta1
Try to install the latest Xcode beta version 12.5 beta
and test it using a simulator running iOS 14.5
Xcode beta link: https://developer.apple.com/download/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With