【问题标题】:UICollectionView Cell Expansion Animation ProblemUICollectionView 单元格展开动画问题
【发布时间】:2020-05-19 20:12:49
【问题描述】:

我遇到了一个问题,我的 UICollectionView 使用 UICollectionViewCompositionalLayout + Diffable 数据源不允许我平滑地为垂直单元格展开动画。我有一列类似于 UITableView 的全角单元格,我正在尝试扩展单元格以在单元格选择时显示截断的文本。

这在没有动画的情况下可以正常工作,但是当我尝试对其进行动画处理时,如果扩展单元格下方的单元格最终位置超出屏幕边界,我无法让它看起来流畅,因为单元格会立即出现消失而不是被动画向下“推”。这个问题可以在链接的gif中看到https://imgur.com/a/ulj0p6n

如果扩展单元格下方的单元格的最终位置在屏幕边界内,则不会出现此问题,如下所示:https://imgur.com/a/uJ2RRRn

我发现了一个似乎有类似问题的旧问题:UICollectionView animating cell size change causes undesired behavior

那里的答案很老,对我没有帮助,因为我没有使用 Flow Layout,也没有使用 Obj-C。

我看到许多应用程序(例如 Instagram,当图像标题具有“查看更多”时)使用 UICollectionView 实现了预期的结果而没有任何动画问题,所以我认为这一定是可能的。

【问题讨论】:

    标签: ios swift uicollectionview uikit uicollectionviewcompositionallayout


    【解决方案1】:

    如果不查看任何代码就很难诊断您的问题,因此这里有一个示例,证明使用UICollectionViewCompositionalLayoutUICollectionViewDiffableDataSource 可以实现具有平滑动画的垂直扩展单元格。

    工作原理

    有两个类值得注意:

    1. Cell:这是垂直扩展的UICollectionViewCell
    2. ViewController:这是配置集合视图、数据源等的UIViewController

    当通过collectionView(_:didSelectItemAt:) 选择Cell 时,items 数组中对应项的isExpanded 属性将切换,并且通过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()
        }
    }
    

    【讨论】:

    • 感谢您提供出色的答案和示例,我能够使用它达到我想要的结果。我还有一个问题,是否可以调整 diffable 动画?就我现在的情况而言,扩展单元格下方的内容有点太慢了,无法移开,并且在动画过程中瞬间出现了一些难看的重叠。
    • 不客气!我认为使用apply(_:animatingDifferences:completion:) 时无法提供自定义动画。
    • @JWK 谢谢你的例子,但你知道为什么boundarySupplementaryItems 在折叠/展开动画中被隐藏了吗?
    • @Carsten 我在这里回答了你的问题:stackoverflow.com/questions/64790617/…
    猜你喜欢
    • 2017-04-04
    • 1970-01-01
    • 2017-05-03
    • 2019-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多