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.