Kamini Sharma
Kamini Sharma

Reputation: 41

Animate images with fade effect in Collection view cells

Note: I have tried all matching questions from stack overflow but none helped to fix this issue/scenario hence posting.

I have a collection view in which i have number of cells where each cell has one image view. Each cell will have different array of images which i need to show one after another through fade effect. First time it shows correctly however when i scroll then problems arises. First cell's image/s displays in any other cell. Images shuffles.


extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
//        let image = cell.viewWithTag(1) as! UIImageView
//        let imgArr =  imagesArray[indexPath.row]
//        image.downloadImageAndAnimateIt(imageStringArray: imgArr)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = self.collectionView.bounds.size.width / 2 - 5
        return CGSize(width: size, height: size - 5)
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        let image = cell.viewWithTag(1) as! UIImageView
        let imgArr =  imagesArray[indexPath.row]
        image.downloadImageAndAnimateIt(imageStringArray: imgArr)

    }


}
//=========== END of view controller

extension UIImageView {

    func downloadImageAndAnimateIt(imageStringArray:[String]){

        var imagesArray = [UIImage]()
        for imgStr in imageStringArray{
            imagesArray.append(UIImage(named: imgStr)!)
        }        

        let dispatchGroup = DispatchGroup()
//        dispatchGroup.notify(queue: DispatchQueue.main) {[weak self] in
//            self?.animationImages = imagesArray.compactMap({$0})
//            self?.animationDuration = 5.0
//            self?.startAnimating()
//        }

        var photoCount = 0        
        var timer = Timer()

        dispatchGroup.notify(queue: DispatchQueue.main) {[weak self] in

            timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { (timer) in
                if (photoCount < imagesArray.count - 1){
                    photoCount = photoCount + 1;
                }else{
                    photoCount = 0;
                }
                if let this = self {
                    UIView.transition(with: this, duration: 2.0, options: .transitionCrossDissolve, animations: {
                        this.image = imagesArray[photoCount]
                    }, completion: nil)
                }
            })

        }

    }

}

Images should not shuffle and those should remain on the same cell as it was before scrolling.

Upvotes: 4

Views: 1684

Answers (3)

Jaydeep Vyas
Jaydeep Vyas

Reputation: 4470

You have to create CustomCollectionCell class to seperate your code try using following way and also for download image you can use SDWebImage

extension ViewController: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {

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

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath) as! CollectionViewCell

        cell.imgView.image = nil
        cell.createImages(imageStringArray: imagesArray[indexPath.row])
        cell.configureTimer()
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = self.collectionView.bounds.size.width / 2 - 5
        return CGSize(width: size, height: (size * 1.5) - 5)
    }



}

class CollectionViewCell: UICollectionViewCell {

    override func awakeFromNib()
    {
        super.awakeFromNib()
        // Initialization code

    }
    var imageArray:[UIImage] = []
    var timer = Timer()
    var photoCount: Int = 0
    @IBOutlet weak var imgView: UIImageView!
    func createImages(imageStringArray:[String])
    {
        self.imageArray.removeAll()
        for imgStr in imageStringArray
        {
          self.imageArray.append(UIImage.init(named: imgStr) ?? UIImage.init())
        }

    }
    func configureTimer()
    {
        self.timer.invalidate()
        self.imgView.image = self.imageArray[self.photoCount] //Set Default Image First Time
        self.timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { (timer) in
            if (self.photoCount < self.imageArray.count - 1){
                self.photoCount = self.photoCount + 1;
            }else{
                self.photoCount = 0;
            }
            if let imageView = self.imgView {
                UIView.transition(with: imageView, duration: 2.0, options: .transitionCrossDissolve, animations: {
                    imageView.image = self.imageArray[self.photoCount]
                }, completion: nil)
            }
        })
    }
}

Upvotes: 2

Atul
Atul

Reputation: 698

There are many cases you will need to handle while downloading an image and showing it inside UITableView/ UICollectionView cell:

  1. Threading: Image should be assigned to UIImageView on main thread.
  2. After download completes, check if cell is still visible and you are assigning it to correct index.

I will strongly recommend to use one of the libraries for this like SDWebImage

Upvotes: 1

Abu Ul Hassan
Abu Ul Hassan

Reputation: 1396

What is actually happening here is collectionView is reusing previous cell so image from previous cell is loaded in new cell when image is under downloading to remove this behavior you need to set a placeholder image Or you may set image empty

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionCell", for: indexPath)
        let image = cell.viewWithTag(1) as! UIImageView
         image.image = "placeholderImage" // replace it with your place holder image 
//Or 
//     image.image = UIImage()
        let imgArr =  imagesArray[indexPath.row]
        image.downloadImageAndAnimateIt(imageStringArray: imgArr)
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = self.collectionView.bounds.size.width / 2 - 5
        return CGSize(width: size, height: size - 5)
    }

    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {


    }

Upvotes: 0

Related Questions