iOS 14.3 上的 UICollectionViewCompositionalLayout 错误
UICollectionViewCompositionalLayout bug on iOS 14.3
我在 iOS 14.3 上遇到一个奇怪的布局问题,集合视图使用 UICollectionViewCompositionalLayout
在我的例子中与 UICollectionViewDiffableDataSource
.
问题是当你有一个正交部分前面有一个固有高度部分时,内部 _UICollectionViewOrthogonalScrollerEmbeddedScrollView
的错误位置。
幸运的是我能够很容易地重现这个问题。
考虑使用此数据源:
private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
enum Section: Int, Hashable, CaseIterable {
case first = 0
case second = 1
}
为每个部分创建以下布局:
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
}
}
}
破坏布局的是 .first
部分 itemSize
和 groupSize
的高度 .estimated
。
你可以在iOS 14.3看到下面的结果:乍一看布局在视觉上是正确的,但你马上意识到它坏了,因为内部滚动视图在错误的位置。
这意味着水平滚动发生在蓝色区域是错误的。
运行 完全相同的代码 直到 iOS 14.2 你得到了正确的布局。
你怎么看待这个问题?
我是不是遗漏了什么或者可能是 UIKit 错误?
谢谢
我们有 header 的估计高度和一些 _UICollectionViewOrthogonalScrollerEmbeddedScrollView
的部分由于这种回归而完全损坏。所以这是一个适用于我们案例的解决方案
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
}
}
}
我们有一个稍微不同的用例,其中带有滚动视图的部分也使用估计高度,所以我修改了 softenhard
的解决方案以同时调整滚动视图的高度。
public final class CollectionView: UICollectionView {
override public 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(),
let maxHeight = scrollView.subviews.map(\.frame.height).max(),
minY > scrollView.frame.minY || maxHeight > scrollView.frame.height
else { return }
scrollView.contentInset.top = -minY
scrollView.frame.origin.y = minY
scrollView.frame.size.height = maxHeight
}
}
}
对我来说,这个问题在 iOS 14.3
和 14.4
中可以重现。现在固定在 iOS 14.5 beta1
尝试安装最新的 Xcode 测试版 12.5 beta
并使用模拟器进行测试 运行 iOS 14.5
Xcode 测试版 link: https://developer.apple.com/download/
我遇到了同样的问题,其他答案的解决方法对我不起作用。
所以我做了这个扩展来确定它是否是 bug iOS-版本:
extension UICollectionView {
static var isIosVersionWithSizeEstimationBug: Bool {
if #available(iOS 14.5, *) {
return false
}
if #available(iOS 14.3, *) {
return true
}
return false
}
}
在这种情况下,我使用它来使用绝对高度。它可能不是适用于每个用例的解决方法。但对我来说这是一个稳定的解决方案:
let height: NSCollectionLayoutDimension = {
let maxPossibleHeight: CGFloat = 280
if UICollectionView.isIosVersionWithSizeEstimationBug {
return .absolute(maxPossibleHeight)
} else {
return .estimated(maxPossibleHeight)
}
}()
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: height)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: height)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
let section = NSCollectionLayoutSection(group: group)
我在 iOS 14.3 上遇到一个奇怪的布局问题,集合视图使用 UICollectionViewCompositionalLayout
在我的例子中与 UICollectionViewDiffableDataSource
.
问题是当你有一个正交部分前面有一个固有高度部分时,内部 _UICollectionViewOrthogonalScrollerEmbeddedScrollView
的错误位置。
幸运的是我能够很容易地重现这个问题。 考虑使用此数据源:
private var dataSource: UICollectionViewDiffableDataSource<Section, String>!
enum Section: Int, Hashable, CaseIterable {
case first = 0
case second = 1
}
为每个部分创建以下布局:
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
}
}
}
破坏布局的是 .first
部分 itemSize
和 groupSize
的高度 .estimated
。
你可以在iOS 14.3看到下面的结果:乍一看布局在视觉上是正确的,但你马上意识到它坏了,因为内部滚动视图在错误的位置。 这意味着水平滚动发生在蓝色区域是错误的。
运行 完全相同的代码 直到 iOS 14.2 你得到了正确的布局。
你怎么看待这个问题? 我是不是遗漏了什么或者可能是 UIKit 错误?
谢谢
我们有 header 的估计高度和一些 _UICollectionViewOrthogonalScrollerEmbeddedScrollView
的部分由于这种回归而完全损坏。所以这是一个适用于我们案例的解决方案
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
}
}
}
我们有一个稍微不同的用例,其中带有滚动视图的部分也使用估计高度,所以我修改了 softenhard
的解决方案以同时调整滚动视图的高度。
public final class CollectionView: UICollectionView {
override public 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(),
let maxHeight = scrollView.subviews.map(\.frame.height).max(),
minY > scrollView.frame.minY || maxHeight > scrollView.frame.height
else { return }
scrollView.contentInset.top = -minY
scrollView.frame.origin.y = minY
scrollView.frame.size.height = maxHeight
}
}
}
对我来说,这个问题在 iOS 14.3
和 14.4
中可以重现。现在固定在 iOS 14.5 beta1
尝试安装最新的 Xcode 测试版 12.5 beta
并使用模拟器进行测试 运行 iOS 14.5
Xcode 测试版 link: https://developer.apple.com/download/
我遇到了同样的问题,其他答案的解决方法对我不起作用。
所以我做了这个扩展来确定它是否是 bug iOS-版本:
extension UICollectionView {
static var isIosVersionWithSizeEstimationBug: Bool {
if #available(iOS 14.5, *) {
return false
}
if #available(iOS 14.3, *) {
return true
}
return false
}
}
在这种情况下,我使用它来使用绝对高度。它可能不是适用于每个用例的解决方法。但对我来说这是一个稳定的解决方案:
let height: NSCollectionLayoutDimension = {
let maxPossibleHeight: CGFloat = 280
if UICollectionView.isIosVersionWithSizeEstimationBug {
return .absolute(maxPossibleHeight)
} else {
return .estimated(maxPossibleHeight)
}
}()
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: height)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1/3), heightDimension: height)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
let section = NSCollectionLayoutSection(group: group)