I have a Collection View with 5 cells (pages).
First page user always see when opening the Collection View - is Welcome Page, which has UIImageView and 2 label beneath.
After the Collection View Controller did load I want to add a short animation to UIImageView to increase its size by 2 from original.
Here is how I tried to do that:
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.row == 0 {
guard let cell = collectionView.cellForItem(at: indexPath) as? WelcomeOnboardingCollectionViewCell else { return }
cell.animate()
}
}
Here is the animation method:
class WelcomeOnboardingCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
func animate() {
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
self.imageView.transform = CGAffineTransform(scaleX: 2, y: 2)
}
}
}
But animation doesn't happen, I see only the initial small UIImageView, despite print(cell) is working, so the method is called.
I also tried to call cell.animate() in cellForItemAt, but then I see already increased UIImageView without animation and doesn't matter which animation duration was set in animate() method.
Also tried to call the next code in scrollViewDidScroll without any luck :
collectionView.visibleCells.forEach { ($0 as? WelcomeOnboardingCollectionViewCell)?.animate() }
Also tried to call on the cell layoutIfNeeded(), but it doesn't changed anything.
What do I miss?
The desired result: CLICK
Working code after Duncan's suggestion:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WelcomeOnboardingCell", for: indexPath) as! WelcomeOnboardingCollectionViewCell
cell.imageView.image = UIImage(named: "\(slides[indexPath.row].imageName)")
cell.titleLabel.text = slides[indexPath.row].title
cell.subtitleLabel.text = slides[indexPath.row].subtitle
cell.animationPending = true
return cell
}
and CollectionViewCell class:
class WelcomeOnboardingCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
var animationPending = false
override func didMoveToSuperview() {
if animationPending {
DispatchQueue.main.async {
self.animate()
}
}
animationPending = false
}
private func animate() {
UIView.animate(withDuration: 0.3, delay: 0.4, options: .curveEaseInOut) {
self.imageView.transform = CGAffineTransform(scaleX: 2.3, y: 2.3)
self.imageView.alpha = 1.0
}
}
}
Alternative:
Call the animate() in ViewController's viewDidAppear():
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
collectionView.visibleCells.forEach { ($0 as? WelcomeOnboardingCollectionViewCell)?.animate() }
}
CodePudding user response:
Both the collectionView(_:willDisplay:forItemAt:) method and the collectionView(_:cellForItemAt:) method are called before your cell is added to the view hierarchy. You can't trigger animation of a view until it is part of the view hierarchy.
I suggest adding the animation "smarts" to your UICollectionViewCell subclass (or to a content view that you put inside the collection view cell.
Have your collectionView(_:cellForItemAt:) set an "animationPending" property on your cell to true.
Then have your UICollectionViewCell respond to a didMoveToSuperview() call by checking it's "animationPending" flag. If it's true, run your animation code and set the flag back to false.
