ios - UICollectionView 单元格展开动画问题
问题描述
我遇到了一个问题,我的 UICollectionView 使用 UICollectionViewCompositionalLayout + Diffable 数据源不允许我平滑地为垂直单元格展开动画。我有一列类似于 UITableView 的全角单元格,我正在尝试扩展单元格以在单元格选择时显示截断的文本。
这在没有动画的情况下可以正常工作,但是当我尝试对其进行动画处理时,如果扩展单元格下方的单元格将具有超出屏幕边界的最终位置,我无法让它看起来流畅,因为单元格将立即消失而不是被动画“推”下。这个问题可以在链接的 gif https://imgur.com/a/ulj0p6n中看到
如果扩展单元格下方的单元格的最终位置在屏幕边界内,则不会出现此问题,如下所示:https ://imgur.com/a/uJ2RRRn 。
我发现了一个似乎有类似问题的旧问题:UICollectionView animating cell size change cause undesired behavior
那里的答案很老,对我没有帮助,因为我没有使用 Flow Layout,也没有使用 Obj-C。
我看到许多应用程序(例如 Instagram,当图像标题具有“查看更多”时)使用 UICollectionView 实现了预期的结果而没有任何动画问题,所以我认为这一定是可能的。
解决方案
如果不查看任何代码,很难诊断您的问题,因此这里有一个示例,证明可以使用UICollectionViewCompositionalLayout
和实现具有平滑动画的垂直扩展单元格UICollectionViewDiffableDataSource
。
这个怎么运作
有两个类值得注意:
Cell
: 这是垂直扩展UICollectionViewCell
。ViewController
:这是UIViewController
配置集合视图、数据源等的。
当Cell
通过 选择a 时,将切换数组中相应项collectionView(_:didSelectItemAt:)
的属性,并通过 更新数据源的快照:isExpanded
items
updateSnapshot()
代码
使用 Single View App 模板创建一个新的 Xcode 项目并将此代码放入ViewController.swift
文件中:
import UIKit
// MARK: - Cell -
final class Cell: UICollectionViewCell {
static let reuseIdentifier = "Cell"
var isExpanded = false {
didSet { label.numberOfLines = numberOfLines }
}
var numberOfLines: Int { isExpanded ? 0 : 3 }
lazy var label: UILabel = {
let label = UILabel()
label.numberOfLines = numberOfLines
label.frame.size = contentView.bounds.size
label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(label)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
label.sizeThatFits(size)
}
}
// MARK: - UIViewController -
class ViewController: UIViewController {
struct Item: Hashable {
let text: String
var isExpanded = false
private let uuid = UUID()
}
var items: [Item] = [
.init(
text: """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
"""
),
.init(
text: """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.
""",
isExpanded: true
)
]
lazy var collectionView: UICollectionView = {
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createCollectionViewLayout())
collectionView.register(Cell.self, forCellWithReuseIdentifier: Cell.reuseIdentifier)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.contentInset.top = 44
collectionView.backgroundColor = .white
collectionView.delegate = self
return collectionView
}()
lazy var dataSource = UICollectionViewDiffableDataSource<Int, Item>(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.reuseIdentifier, for: indexPath) as? Cell else { fatalError() }
cell.isExpanded = itemIdentifier.isExpanded
cell.label.text = itemIdentifier.text
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
updateSnapshot()
}
private func createCollectionViewLayout() -> UICollectionViewCompositionalLayout {
let layoutSize = NSCollectionLayoutSize.init(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(200)
)
let section = NSCollectionLayoutSection(group:
.vertical(
layoutSize: layoutSize,
subitems: [.init(layoutSize: layoutSize)]
)
)
section.contentInsets = .init(top: 0, leading: 16, bottom: 0, trailing: 16)
section.interGroupSpacing = 20
return .init(section: section)
}
private func updateSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Int, Item>()
snapshot.appendSections([0])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
// MARK: - UICollectionViewDelegate -
extension ViewController: UICollectionViewDelegate {
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let itemIdentifier = dataSource.itemIdentifier(for: indexPath) else { return }
items[indexPath.row] = .init(text: itemIdentifier.text, isExpanded: !itemIdentifier.isExpanded)
updateSnapshot()
}
}
推荐阅读
- php - 如何计算 Naravel 中的正确答案测验?
- postgresql - 在 PostGis 上的 Spring Boot 空间查询中遇到无效的字节序标志值
- java - 未连接适配器;跳过布局 _____ AndroidX RecyclerView
- azure - 我们如何使用服务帐户或用户帐户使用 C# 控制台应用程序提取 Azure keyvault 值
- shell - 如果该行需要更多时间执行,则跳过 shell 脚本中的执行行
- azure - 使用 ARM 模板在 Azure 存储容器中创建目录?
- batch-file - 使用 Plink 和 PSCP 交互
- python - Tensorflow 2:GradientTape 返回无
- sql - 雪花中两个日期之间的工作日
- google-chrome-devtools - 减慢 chrome 开发工具调试器对我的行为的反应