问题描述
我在 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 错误?
谢谢
解决方法
我们有估计高度的标题,并且由于这种回归,一些带有 _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
}
}
}
,
对我来说,这个问题在 iOS 14.3
和 14.4
中是可重现的。现在已在 iOS 14.5 beta1
尝试安装最新的 Xcode 测试版 12.5 beta
并使用运行 iOS 14.5
的模拟器进行测试
Xcode 测试版链接:https://developer.apple.com/download/
,我们有一个稍微不同的用例,其中包含滚动视图的部分也使用估计高度,因此我修改了 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 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 版本:
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)