UICollectionViewCell - contents do not animate alongside cell's contentView

╄→гoц情女王★ 提交于 2019-12-02 18:45:20

This is a finicky problem, and you're very close to the solution. The issue is that the approach to animating layout changes varies depending on whether you're using auto layout or resizing masks or another approach, and you're currently using a mix in your ProblematicCollectionViewCell class. (The other available approaches would be better addressed in answer to a separate question, but note that Apple generally seems to avoid using auto layout for cells in their own apps.)

Here's what you need to do to animate your particular cells:

  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. The simplest way to do that is using performBatchUpdates, which will cause new sizes to be fetched from sizeForItemAt, and will then apply the new layout attributes to the relevant cells within its own animation block:

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.selectedIndex = indexPath.row
        collectionView.performBatchUpdates(nil)
    }
    
  2. Tell your cells to layout their subviews every time the collection view layout object changes their layout attributes (which will occur within the performBatchUpdates animation block):

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

If you want greater control over your animations, you can nest the call to performBatchUpdates inside a call to one of the UIView.animate block-based animation methods. The default animation duration for collection view cells in iOS 10 is 0.25.

The solution is very easy. First, in ViewController.collectionView(_,didSelectItemAt:), write only this:

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

And then, in the class ProblematicCollectionViewCell add this func:

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

    self.layoutIfNeeded()
}

Enjoy!

You can apply a transform to a cell, although it has some drawbacks, such as handling orientation changes.

For extra impact, I have added a color change and a spring effect in the mix, neither of which could be achieved using the reloading route:

    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)
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!