UICollectionViewCell - 内容不会与单元格的 contentView 一起动画

UICollectionViewCell - contents do not animate alongside cell's contentView

问题看起来像这样: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]
}

其他说明: 下面是cell size动画涉及的代码

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: []
}

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

为了增加效果,我在混音中添加了颜色变化和 spring 效果,这两种效果都无法使用重新加载路线实现:

    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)
    }

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

以下是为特定单元格设置动画所需执行的操作:

  1. 选择或取消选择单元格时,请告诉收集布局对象,细胞大小已更改,并在其可以做到的范围内对这些更改进行动画。最简单的方法是使用 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 基于块的动画方法之一的调用中。 iOS10 中集合视图单元格的默认动画持续时间为 0.25。

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

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

然后,在 class ProblematicCollectionViewCell 中添加此函数:

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

    self.layoutIfNeeded()
}

尽情享受吧!