UIImageView animated doesn't work properly inside UICollectionViewCell
Asked Answered
J

6

13

I would like to add a UIImageView animated inside an UICollectionViewCell so I came up with this code:

    import UIKit

class MainViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {

    var items:[String] = ["one", "two", "three", "four"]

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor(red: 220/255, green: 220/255, blue: 220/255, alpha: 1.0)
        self.view.addSubview(self.collectionView)
    }

    lazy var collectionView:UICollectionView = {
        var cv = UICollectionView(frame: self.view.bounds, collectionViewLayout: self.flowLayout)
        cv.delegate = self
        cv.dataSource = self
        cv.bounces = true
        cv.alwaysBounceVertical = true
        cv.autoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth
        cv.registerClass(CustomCell.self, forCellWithReuseIdentifier: "cell")
        cv.backgroundColor = UIColor(red: 220/255, green: 220/255, blue: 220/255, alpha: 1.0)
        return cv
    }()

    lazy var flowLayout:UICollectionViewFlowLayout = {
        var flow = UICollectionViewFlowLayout()
        flow.sectionInset = UIEdgeInsetsMake(2.0, 2.0, 2.0, 2.0)
        return flow
    }()


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize{

        let width:CGFloat = self.view.bounds.size.width*0.98;
        let height:CGFloat = 150.0;
        return CGSizeMake(width, height)
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
        return self.items.count
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CustomCell
        cell.layer.cornerRadius = 4
        cell.backgroundColor = UIColor.whiteColor()
        cell.imgView.animationImages = ["1","2","3","4","5", "6","7","8"].map{UIImage(named: $0)!}
        cell.imgView.animationDuration = NSTimeInterval(0.8)
        cell.imgView.startAnimating()
        return cell
    }

    func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
         return true
    }

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        var alert = UIAlertController(title: "Alert!", message: "Cell tapped", preferredStyle: UIAlertControllerStyle.Alert)
        var action = UIAlertAction(title: "ok", style: UIAlertActionStyle.Cancel) { (dd) -> Void in }
        alert.addAction(action)
        self.presentViewController(alert, animated: true, completion: nil)
    }

}

This is my MainViewController with a CollectionView added programmatically.

    import UIKit

class CustomCell: UICollectionViewCell {

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(self.imgView)
    }

    lazy var imgView:UIImageView = {
        var iv = UIImageView()
        iv.contentMode = UIViewContentMode.ScaleAspectFill
        return iv
    }()

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        self.imgView.frame = CGRectMake(6,15,self.frame.width*0.2, self.frame.height*0.8)
    }
}

And this is my custom UICollectionViewCell with an UIImageView

My problem is when you tap a cell the UIImageView disappears. Trying to solve the problem I started to look another UICoolectionView delegate methods. Then I tried to use shouldHighlightItemAtIndexPath, But If I use this method returning false the animation works fine but the collection view stops responding to didSelectItemAtIndexPath.

This is a github repository with a code that shows the issue: https://github.com/gazolla/ImageAnimation (updated with solution)

SOLUTION:

With matt's help, I made the following changes in my code:

1) add images array to highlightedAnimationImages property

let animationArray = ["1","2","3","4","5", "6","7","8"].map{UIImage(named: $0)!}
cell.imgView.highlightedAnimationImages = animationArray

2) Restart animation when cells are deselected

 func collectionView(collectionView: UICollectionView, didDeselectItemAtIndexPath indexPath: NSIndexPath) {
    if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? CustomCell{
        cell.imgView.startAnimating()
    }
 }

3) Restart animation when cells are selected

func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    let cell = collectionView.cellForItemAtIndexPath(indexPath) as! CustomCell
    cell.imgView.startAnimating()

    //...

}

SOLUTION 2:

After some tests, I found out that when you tap and hold a cell UIImageView disappears again, So we have to restart animation in another two methods:

1) didHighlightItemAtIndexPath

func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
        if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? CustomCell{
            cell.imgView.startAnimating()
        }

  }

2) didUnhighlightItemAtIndexPath

func collectionView(collectionView: UICollectionView, didUnhighlightItemAtIndexPath indexPath: NSIndexPath) {
    if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? CustomCell{
        cell.imgView.startAnimating()
    }
}
Jedlicka answered 2/9, 2015 at 14:47 Comment(2)
I had to use Solution 2.Smattering
Solution 1 works.Slaw
L
3

When you select the cell, the image view looks to its highlightedAnimationImages, not its animationImages. You didn't set the highlightedAnimationImages so you see nothing.

Here's a screencast showing that this can work. The cell is selected when the background is gray (that is its selectedBackgroundView) but the animation continues:

enter image description here

Lubricous answered 2/9, 2015 at 14:51 Comment(6)
Thanks for your answer, matt. I add highlightedAnimationImages into cellForItemAtIndexPath and it doesn't work. Should I put this command in another place ?Jedlicka
I added a github repository with my code in the question.Jedlicka
You also have to start the animation again.Lubricous
Matt, I found out another issue, when you tap and hold a cell sometimes animation disappears. Probably I will have to restart animation in another point. Do you know if we can detect tap and hold uicollectionviewcell ?Jedlicka
Have you considered using an animated image instead of an animated image view? They don't stop ever.Lubricous
My book explains it to you: apeth.com/iOSBook/ch17.html#_uiimageview_and_uiimage_animationLubricous
G
2

In case anyone else encounters this nasty bug and the above solutions don't help – try calling imageView.stopAnimating() in your cell's prepareForReuse() function.

Graniteware answered 13/11, 2017 at 22:30 Comment(0)
S
1

Swift 4 version of Solution 2 from @Sebastian.

func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath) {
    if let cell = collectionView.cellForItem(at: indexPath) as? CustomCell {
        cell.imgView.startAnimating()
    }
}

func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath) {
    if let cell = collectionView.cellForItem(at: indexPath) as? CustomCell {
        cell.imgView.startAnimating()
    }
}
Smattering answered 31/5, 2018 at 1:3 Comment(0)
R
0

I had the same problem, but i did not get the "didDeselectItemAtIndexPath" to work, so my solution to the problem was just to add a button with no function over the picture.

So when you touch the picture, you will touch the button instead.

Relinquish answered 9/7, 2018 at 15:6 Comment(0)
S
0

The solution 1 and 2 based on Sebastian gives me an inspiration. When UITableViewCell isSelected or isHighlighted status changes, it will call setSelected:animated: or setHighlighted:animated method. So, I override the methods of custom UITableViewCell.

class DCFWFailReasonCell: UITableViewCell {

    // Local flag for UIImageView animation.
    fileprivate var isAnimation: Bool = false

    // Setup animation images 
    func configure(animateImage: [UIImage], duration: TimeInterval) {
        imgView.animationImages = animateImage
        imgView.highlightedAnimationImages = animateImage
        
        imgView.animationDuration = duration
        imgView.animationRepeatCount = 0 //< 0 infinite
        
        imgView.startAnimating()

        // imgView has animation.         
        isAnimation = true
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
        // 
        if (self.isAnimation) {
            imgView.startAnimating()
        }
    }
    
    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
        super.setHighlighted(highlighted, animated: animated)
        
        if (self.isAnimation) {
            imgView.startAnimating()
        }
    }

}
Slaw answered 21/4, 2021 at 8:17 Comment(0)
J
0

Here is my solution, works well

class MyImageView: UIImageView {
    
    override func stopAnimating() {
        super.stopAnimating()
        startAnimating()
    }
}
Jovanjove answered 19/5, 2021 at 15:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.