Home > OS >  UIImageView animation inside UICollectionViewCell doesn't work
UIImageView animation inside UICollectionViewCell doesn't work

Time:05-06

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.

  • Related