Sobes.tech
Middle

How can flickering of images be eliminated when scrolling table cells?

sobes.tech AI

Answer from AI

The flickering of images when scrolling UITableView or UICollectionView can be mitigated by the following methods:

  • Image caching: Use NSCache or URLCache to store loaded images by URL. Check the cache before requesting an image.

    import UIKit
    
    let imageCache = NSCache<NSURL, UIImage>()
    
    func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        // Check cache before loading
        if let cachedImage = imageCache.object(forKey: url as NSURL) {
            completion(cachedImage)
            return
        }
    
        // Load image asynchronously
        URLSession.shared.dataTask(with: url) { data, response, error in
            guard let data = data, error == nil else {
                completion(nil)
                return
            }
    
            if let loadedImage = UIImage(data: data) {
                // Cache the loaded image
                imageCache.setObject(loadedImage, forKey: url as NSURL)
                completion(loadedImage)
            } else {
                completion(nil)
            }
        }.resume()
    }
    
  • Asynchronous loading: Load images in a background thread to avoid blocking the main UI thread. Use URLSession or third-party libraries (e.g., Kingfisher, SDWebImage, Nuke).

    import UIKit
    
    func configureImageView(_ imageView: UIImageView, with url: URL) {
        imageView.image = nil // Clear image before loading
    
        // Example with URLSession (can use third-party libraries)
        loadImage(from: url) { loadedImage in
            // Update UI on main thread
            DispatchQueue.main.async {
                imageView.image = loadedImage
            }
        }
    }
    
  • Placeholder image: Show a temporary placeholder image until the main image is fully loaded.

    // In cellForRowAt / cellForItemAt
    cell.imageView.image = UIImage(named: "placeholder") // Set placeholder
    
    // When image is loaded
    DispatchQueue.main.async {
        cell.imageView.image = loadedImage // Set loaded image
    }
    
  • Cancel loading when reusing cells: Cancel the current image load in prepareForReuse() if it is still ongoing. This prevents setting the wrong image in the cell.

    class CustomCell: UITableViewCell {
        let customImageView = UIImageView()
        private var dataTask: URLSessionDataTask?
    
        override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            // Setup customImageView
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        func configure(with url: URL) {
            customImageView.image = UIImage(named: "placeholder")
            dataTask?.cancel() // Cancel previous load
    
            dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
                guard let data = data, error == nil else { return }
    
                if let loadedImage = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self.customImageView.image = loadedImage
                    }
                }
            }
            dataTask?.resume()
        }
    
        override func prepareForReuse() {
            super.prepareForReuse()
            customImageView.image = nil // Clear or set placeholder
            dataTask?.cancel() // Cancel load on reuse
        }
    }
    
  • Using Kingfisher, SDWebImage, or Nuke: These libraries provide ready-made solutions for caching, asynchronous loading, and canceling loads, greatly simplifying image handling.

    import Kingfisher
    
    // Example with Kingfisher
    let url = URL(string: imageName)!
    imageView.kf.indicatorType = .activity // Show loading indicator
    imageView.kf.setImage(
        with: url,
        placeholder: UIImage(named: "placeholder"),
        options: [
            .transition(.fade(1)) // Fade animation when setting image
        ])
    

When these techniques are properly applied, flickering during scrolling will be minimized.