【问题标题】:UICollectionViewCell - contents do not animate alongside cell's contentViewUICollectionViewCell - 内容不会与单元格的 contentView 一起动画
【发布时间】:2017-04-02 12:16:47
【问题描述】:

问题看起来像这样:http://i.imgur.com/5iaAiGQ.mp4 (红色是cell.contentView的颜色)

这里是代码:https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem

当前状态: UICollectionViewCell 的 contentView 的内容不会随着 contentView 帧的变化而动画。它立即获得大小而无需动画。

执行任务时遇到的其他问题: 在我在 UICollectionViewCell 子类中执行此操作之前,contentView 也没有与单元格的框架更改一起动画:

override func awakeFromNib() {
    super.awakeFromNib()

    //Because contentView won't animate along with cell
    contentView.frame = bounds
    contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}

其他说明: 这是单元格大小动画中涉及的代码

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    self.selectedIndex = indexPath.row

    collectionView.performBatchUpdates({
        collectionView.reloadData() 
    }, completion: nil)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let isSelected = self.selectedIndex == indexPath.row
    let someSize : CGFloat = 90 //doesn't matter
    let sizeK : CGFloat = isSelected ? 0.9 : 0.65
    let size = CGSize(width: someSize * sizeK, height: someSize * sizeK)

    return size
}

使用collectionView.setCollectionViewLayout(newLayout, animated: true) 时我得到相同的结果,而使用collectionView.collectionViewLayout.invalidateLayout() 而不是reloadData() inside batchUpdates 时根本没有动画。

更新 当我在 UICollectionView 的 willDisplayCell 方法中打印 imageView.constraints 时,它会打印空数组。

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    for view in cell.contentView.subviews {
        print(view.constraints)
    }

    //Outputs
    //View: <UIImageView: 0x7fe26460e810; frame = (0 0; 50 50); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x608000037280>>
    //View constraints: []
}

【问题讨论】:

    标签: ios animation uicollectionview uicollectionviewcell


    【解决方案1】:

    这是一个棘手的问题,您已经非常接近解决方案了。问题是动画布局更改的方法取决于您是使用自动布局还是调整掩码大小或其他方法,并且您当前在 ProblematicCollectionViewCell 类中使用混合。 (其他可用方法最好在回答单独的问题时解决,但请注意,Apple 通常似乎避免在自己的应用程序中对单元格使用自动布局。)

    您需要执行以下操作来为特定单元格设置动画:

    1. When cells are selected or deselected, tell the collection view layout object that cell sizes have changed, and to animate those changes to the extent it can do so.最简单的方法是使用performBatchUpdates,这将导致从sizeForItemAt 获取新的尺寸,然后将新的布局属性应用到其自己的动画块中的相关单元格:

      func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
          self.selectedIndex = indexPath.row
          collectionView.performBatchUpdates(nil)
      }
      
    2. 每次集合视图布局对象更改其布局属性(将在performBatchUpdates 动画块中发生)时,告诉您的单元格布局其子视图:

      // ProblematicCollectionViewCell.swift
      
      override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
          super.apply(layoutAttributes)
          layoutIfNeeded()
      }
      

    如果您想更好地控制动画,可以将对 performBatchUpdates 的调用嵌套在对 UIView.animate 基于块的动画方法之一的调用中。 iOS 10 中集合视图单元格的默认动画持续时间为 0.25。

    【讨论】:

    • 作为参考,当使用 UICollectionViewFlowLayout 在集合视图上调用performBatchUpdates(nil) 时,传递给invalidateLayout(with:) 的上下文配置为invalidateEverythinginvalidateDataSourceCounts 设置为false,但@987654333 @ 和 invalidateFlowLayoutDelegateMetrics 设置为 true。效果是流布局对象要求新的大小信息并重新计算项目和视图的位置和大小,而不会使所有内容失效。
    【解决方案2】:

    您可以将transform 应用于单元格,尽管它有一些缺点,例如处理方向更改。

    为了获得额外的效果,我在混合中添加了颜色变化和弹簧效果,这两者都不能使用重新加载路线实现:

        func collectionView(_ collectionView: UICollectionView,
                            didSelectItemAt indexPath: IndexPath) {
            UIView.animate(
                withDuration: 0.4,
                delay: 0,
                usingSpringWithDamping: 0.4,
                initialSpringVelocity: 0,
                options: UIViewAnimationOptions.beginFromCurrentState,
                animations: {
                    if( self.selectedIndexPath.row != NSNotFound) {
                        if let c0 =
                            collectionView.cellForItem(at: self.selectedIndexPath)
                        {
                            c0.contentView.layer.transform = CATransform3DIdentity
                            c0.contentView.backgroundColor = UIColor.lightGray
                        }
                    }
                    self.selectedIndexPath = indexPath
                    if let c1 = collectionView.cellForItem(at: indexPath)
                    {
                        c1.contentView.layer.transform =
                            CATransform3DMakeScale(1.25, 1.25, 1)
                        c1.contentView.backgroundColor = UIColor.red
                    }
    
                },
                completion: nil)
        }
    

    【讨论】:

    • 感谢您的详细解释,但您的选择不会让设计师满意,因此,客户
    • 只是为了澄清 - 我的意思是细胞之间的距离。在您的解决方案中,您增加了它,以便当某些单元格的大小增长时,它适合布局。但它有点hacky,看起来也很hacky
    【解决方案3】:

    解决方案非常简单。首先,在 ViewController.collectionView(_,didSelectItemAt:) 中,只写这个:

    collectionView.performBatchUpdates({
            self.selectedIndex = indexPath.row
        }, completion: nil)
    

    然后,在 ProblematicCollectionViewCell 类中添加这个函数:

    override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
        super.apply(layoutAttributes)
    
        self.layoutIfNeeded()
    }
    

    享受吧!

    【讨论】:

    • 谢谢,它有效,但 Jamesk 是第一个给出正确答案的人
    猜你喜欢
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-02
    相关资源
    最近更新 更多