17

我在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.3 布局损坏

在 iOS 14.2 之前运行完全相同的代码,您将获得正确的布局。 正确布局

你怎么看这个问题?我是否遗漏了什么或者它可能是一个 UIKit 错误?

谢谢

4

4 回答 4

11

由于这种回归,我们有估计高度的标题和一些部分_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
        }
    }
}
于 2021-01-17T13:51:22.323 回答
6

对我来说,这个问题在 iOS14.314.4. 现在固定在iOS 14.5 beta1

尝试安装最新的 Xcode beta 版本12.5 beta并使用正在运行的模拟器进行测试iOS 14.5

Xcode 测试版链接:https ://developer.apple.com/download/

于 2021-02-02T09:49:46.063 回答
5

我们有一个稍微不同的用例,其中带有滚动视图的部分也使用估计的高度,所以我修改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
        }
    }
}
于 2021-01-28T16:32:17.317 回答
1

我遇到了同样的问题,其他答案的解决方法对我不起作用。

所以我做了这个扩展来确定它是否是有问题的 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)
于 2021-03-22T14:07:42.573 回答