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

时间:2016-11-18 15:21:02

标签: ios animation uicollectionview uicollectionviewcell

问题如下所示:http://i.imgur.com/5iaAiGQ.mp4 (红色是cell.contentView的颜色)

以下是代码:https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem

当前状态: UICollectionViewCell的contentView的内容不会与contentView框架更改一起动画。它会在没有动画的情况下立即获得大小。

执行任务时遇到的其他问题: contentView也没有动画细胞的帧更改,直到我在UICollectionViewCell子类中执行此操作:

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的imageView.constraints方法中打印willDisplayCell时,它会打印出空数组。

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

3 个答案:

答案 0 :(得分:21)

这是一个棘手的问题,你非常接近解决方案。问题是,布局更改动画的方法取决于您是使用自动布局还是调整大小掩码或其他方法,并且您当前正在使用ProblematicCollectionViewCell类中的混合。 (其他可用的方法可以更好地解决一个单独的问题,但请注意,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()
    }
    
  3. 如果您希望更好地控制动画,可以在对performBatchUpdates基于块的动画方法之一的调用中嵌套对UIView.animate的调用。 iOS 10中集合视图单元格的默认动画持续时间为0.25。

答案 1 :(得分:5)

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

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

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

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

    self.layoutIfNeeded()
}

享受!

答案 2 :(得分:1)

您可以将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)
    }