ios UICollectionViewCompositionLayout动态高度导致布局调整和 Flink

liwlm1x9  于 2023-08-08  发布在  iOS
关注(0)|答案(1)|浏览(142)

我正在使用UICollectionViewCompositionalLayout在我的iOS应用中创建一个水平滚动(正交滚动)列表的垂直滚动列表。
当我为项目和组指定估计高度时,集合视图将在较高的项目滚动到视图中时调整其高度。
另一方面,如果我为组指定估计高度,为项目指定分数高度为1.0,则无论设置的约束和抗压性如何,高度似乎都被视为绝对值。
下面是我的createSection函数:

func createSection(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .absolute(90),
        heightDimension: .estimated(44)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(90 / layoutEnvironment.container.effectiveContentSize.width),
        heightDimension: .estimated(44)
    )
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 16.0
    section.orthogonalScrollingBehavior = .continuous
    return section
}

字符串
我尝试将UICollectionViewCompositionalLayout子类化,并覆盖layoutAttributesForElementsInRect,以根据每个部分中最高的项调整属性。然而,这导致了滚动期间的 Flink ,我怀疑这是由于prepare在边界变化时被连续调用。
我事先不知道每个单元的视野高度。然而,我已经确保它们被正确地约束,并且压缩阻力被设置为垂直轴上的所需。
预期行为:每个单元格的高度将根据其内容动态调整,而不会在不同高度的单元格进入视野时引起布局变化或 Flink 。
实际行为:为项目和组指定估计高度会导致滚动时布局更改,而为项目设置分数高度会导致组的估计高度被视为绝对值。覆盖layoutAttributesForElementsInRect并在那里进行调整会导致 Flink 。
如何在UICollectionViewCompositionalLayout中处理动态单元格高度而不引起这些布局问题?我将非常感谢任何建议或潜在的解决方案。
下面是Swift Playground演示了这个问题:https://github.com/aodhol/CompositionalProblem/tree/main

显示估计尺寸过大和过小的屏幕截图:

100d1x

的字符串

mqxuamgl

mqxuamgl1#

在iOS世界中,UICollectionViewCompositionalLayout的设计并不完全是为了在向组的方向滚动时处理不同大小的单元格。当您设置了正交滚动行为时,通常认为正交滚动组中的所有项目大小相同。
不过,别担心,我们有办法。我们可以对内部列表使用UICollectionViewFlowLayout,而不是对外部垂直列表和内部水平列表都使用UICollectionViewCompositionalLayout。这种方法非常灵活,可以处理可变的单元格大小。但是,请记住,如果您有大量的数据,这可能不是最有效的解决方案。
让我们像这样重构createSection函数:

func createSection(sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection {
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(44)
    )
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .estimated(44)
    )
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 16.0
    return section
}

字符串
然后,在处理UICollectionViewDataSource时,您将像往常一样出列并设置单元。但这里有一个转折-在每个单元格中,你会有一个嵌套的UICollectionView使用UICollectionViewFlowLayout

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCell.identifier, for: indexPath) as! MyCell

    let flowLayout = UICollectionViewFlowLayout()
    flowLayout.scrollDirection = .horizontal
    flowLayout.estimatedItemSize = CGSize(width: 90, height: 44)
    
    cell.nestedCollectionView.collectionViewLayout = flowLayout
    cell.nestedCollectionView.dataSource = self
    cell.nestedCollectionView.delegate = self
    cell.nestedCollectionView.reloadData()
    
    return cell
}


最后,确保你的单元格可以处理嵌套的UICollectionView

class MyCell: UICollectionViewCell {
    static let identifier = "MyCell"
    
    let nestedCollectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        return collectionView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(nestedCollectionView)
        
        NSLayoutConstraint.activate([
            nestedCollectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            nestedCollectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
            nestedCollectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
            nestedCollectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


这个解决方案应该能提供您所需要的行为,但是请记住,它需要管理两个集合视图。此外,您可能会遇到一些需要解决的其他挑战。如果你有大量的细胞,这个解决方法可能会有点慢,所以它不是随便使用的。

相关问题