Question or problem in the Swift programming language:
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() } }
How to solve the problem:
Solution 1:
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:
Solution 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.
Solution 3:
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() } }
Solution 4:
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.