iosswiftcachingavplayersdwebimage

swift Video Thumbnail image cache problem


I have VideoThumbnailManager and It serves to cache with SDWebImage the image of the video and display it to the user. But the Video thumbnail is showing 2 on the collectionview. but when I scroll, the collectionview changes and becomes correct. how do i solve this problem?

result :

enter image description here

//
//  VideoThumbnailManager.swift
//  Created by erhan demirci on 26.04.2023.




import Foundation
import AVFoundation
import SDWebImage

class VideoThumbnailManager {
    
    
    static let shared = VideoThumbnailManager()
    
    
    func generateThumbnailImage(for videoURL: URL, completion: @escaping (UIImage?) -> Void) {
        let options: SDWebImageOptions = [.scaleDownLargeImages, .continueInBackground]
        let cacheKey = SDWebImageManager.shared.cacheKey(for: videoURL)
        
        if let cachedImage = SDImageCache.shared.imageFromCache(forKey: cacheKey) {
            // Return cached image if available
            completion(cachedImage)
        } else {
            // Generate new thumbnail image
            let asset = AVAsset(url: videoURL)
            let imageGenerator = AVAssetImageGenerator(asset: asset)
            imageGenerator.appliesPreferredTrackTransform = true
            imageGenerator.maximumSize = CGSize(width: 100, height: 100)
            let timestamp = CMTime(seconds: 1.0, preferredTimescale: 60)
            imageGenerator.generateCGImagesAsynchronously(forTimes: [NSValue(time: timestamp)]) { (requestedTime, cgImage, actualTime, result, error) in
                if let cgImage = cgImage {
                    let thumbnailImage = UIImage(cgImage: cgImage)
                    completion(thumbnailImage)
                    // Cache thumbnail image
                    if let data = thumbnailImage.jpegData(compressionQuality: 0.8) {
                        SDImageCache.shared.storeImageData(toDisk: data, forKey: cacheKey)
                    }
                } else {
                    completion(nil)
                }
            }
        }
    }
    
}

my collection view cell

class TemplateCell: UICollectionViewCell {
   

    
    @IBOutlet weak var thumbnailImageView: UIImageView!
    


    func configure(with videoURL: URL) {
        
        self.videoURL = videoURL
     
        
       
       }
    
 
    
    
       func loadVideo() {
          
           guard let videoURL = self.videoURL else {
                      return
               
           }
           

           // use the VideoCacheManager to get the video asset and thumbnail image
           VideoThumbnailManager.shared.generateThumbnailImage(for: videoURL) { thumbnail in
               DispatchQueue.main.async {
                   self.thumbnailImageView.image = thumbnail
                   
               }
               
           }

       }
       func unloadVideo() {
           self.videoPlayerLayer?.removeFromSuperlayer()
           //self.videoPlayerLayer = nil
        }
}

I use on collection view like that:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    ///....
    cell.configure(with: myUrl)

}


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

        
    }

    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
      
    
    videoCell.unloadVideo()
       
        
    }

Solution

  • You can read this answer to understand why this is happening. https://stackoverflow.com/a/76261182/5973760

    There are different ways to get this done, try to implement below code.

    VideoThumbnailManager.shared.generateThumbnailImage(for: videoURL) { (thumbnail,thubmVideoURL) in
                       DispatchQueue.main.async {
                           if thubmVideoURL == videoURL{
                               self.thumbnailImageView.image = thumbnail
                           }
                       }
                       
                   }
    

    Here you need to change the completion of your generateThumbnailImage() function along with image pass the video url also.

    completion(thumbnailImage,videoURL)
    

    You can check the other approach also: https://stackoverflow.com/a/31844232/5973760