Problem looks like this: http://i.imgur.com/5iaAiGQ.mp4 (red is a color of cell.contentView)
Here is the code: https://github.com/nezhyborets/UICollectionViewContentsAnimationProblem
Current status: The content of UICollectionViewCell's contentView does not animate alongside contentView frame change. It gets the size immediately without animation.
Other issues faced when doing the task: The contentView was not animating alongside cell's frame change either, until i did this in UICollectionViewCell subclass:
override func awakeFromNib() {
super.awakeFromNib()
//Because contentView won't animate along with cell
contentView.frame = bounds
contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
Other notes: Here is the code involved in cell size animation
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
}
I get the same results when using collectionView.setCollectionViewLayout(newLayout, animated: true)
, and there is no animation at all when using collectionView.collectionViewLayout.invalidateLayout()
instead of reloadData() inside batchUpdates
.
UPDATE
When I print imageView.constraints
inside UICollectionView's willDisplayCell
method, it prints empty array.
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: []
}
The solution is very easy. First, in ViewController.collectionView(_,didSelectItemAt:), write only this:
And then, in the class ProblematicCollectionViewCell add this func:
Enjoy!
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:
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 fromsizeForItemAt
, and will then apply the new layout attributes to the relevant cells within its own animation block: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):If you want greater control over your animations, you can nest the call to
performBatchUpdates
inside a call to one of theUIView.animate
block-based animation methods. The default animation duration for collection view cells in iOS 10 is 0.25.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: