使用Compositional Layout和Diffable DataSource移除UICollectionView分区时异常
可移除分区的UICollectionView正交滚动分区动画异常问题
场景说明
UICollectionView包含两个分区:
- 第一个分区仅含一个单元格,点击该单元格会触发快照更新以移除该分区
- 第二个分区包含多个可正交滚动的单元格
问题现象
滚动第二个分区后,点击第一个分区的单元格时:
- 预期行为:第一个分区平滑移除,第二个分区滑动至原第一个分区的位置
- 实际现象:正交滚动分区出现闪烁、跳动,直到动画结束
效果对比
| 预期效果(未滚动时) | 异常效果(实际结果) |
|---|---|
![]() | ![]() |
测试代码(Xcode 15.3,iOS 17,可直接在Playground运行)
import UIKit import PlaygroundSupport var onTap = { removeSection() } // Rainbow colored cells final class Cell: UICollectionViewCell { override func prepareForReuse() { contentView.backgroundColor = .clear } func configure(with color: UIColor) { contentView.backgroundColor = color } } // The cell that when tapped, triggers the section removal (note that selection isn't being handled through didSelectItem. final class RemovableCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) contentView.backgroundColor = .systemCyan contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap))) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc func tap() { onTap() } } // Registrations for the cells let cellRegistration = UICollectionView.CellRegistration<Cell, Int> { cell, indexPath, _ in let colors: [UIColor] = [.red, .orange, .yellow, .green, .blue] cell.configure(with: colors[indexPath.item % colors.count]) } let removableCellRegistration = UICollectionView.CellRegistration<RemovableCell, Int> { cell, indexPath, _ in } let removableSection: NSCollectionLayoutSection = { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .estimated(120)) let item = NSCollectionLayoutItem(layoutSize: itemSize) let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) return section }() // Sections definitions let orthogonallyScrollingSection: NSCollectionLayoutSection = { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .estimated(100), heightDimension: .fractionalHeight(0.2)) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous return section }() // Collection view construction, please don't mind the mess. var removeSectionAvailable = true let layout = UICollectionViewCompositionalLayout { section, _ in if section == 0, removeSectionAvailable { removableSection } else { orthogonallyScrollingSection } } let frame = CGRect(x: 0, y: 0, width: 300, height: 500) let view = UICollectionView(frame: frame, collectionViewLayout: layout) let dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: view) { collectionView, indexPath, itemIdentifier in if indexPath.section == 0, removeSectionAvailable { return collectionView.dequeueConfiguredReusableCell( using: removableCellRegistration, for: indexPath, item: itemIdentifier) } else { return collectionView.dequeueConfiguredReusableCell( using: cellRegistration, for: indexPath, item: itemIdentifier ) } } var snapshot = NSDiffableDataSourceSnapshot<Int, Int>() snapshot.appendSections([0, 1]) snapshot.appendItems([0], toSection: 0) snapshot.appendItems([1, 2, 3, 4, 5, 6], toSection: 1) dataSource.apply(snapshot) func removeSection() { removeSectionAvailable = false snapshot.deleteSections([0]) dataSource.apply(snapshot) } // Playground setup PlaygroundPage.current.liveView = view PlaygroundPage.current.needsIndefiniteExecution = true
恳请各位提供解决思路!
内容的提问来源于stack exchange,提问作者Renzo Tissoni
相关产品推荐
相关产品推荐



