“Image Flickering” or “Cell Reuse Bug,” occurs when you scroll a UITableView or UICollectionView rapidly, and you see the image from one cell appearing in another cell that should display a different image. This happens because cells in table views and collection views are reused to optimize memory and performance.
When you scroll a table view or collection view, the system dequeues cells that have gone offscreen and reuses them for new content. However, during the process of dequeuing and reusing cells, if the content of the cell is not updated properly, the previously loaded image may still be visible before the new image is loaded.
To understand and solve this issue, you need to consider the following:
- Cell Reuse: Table views and collection views reuse cells to minimize memory usage and improve scrolling performance. Cells that are scrolled offscreen are dequeued and reused for new content. However, if you don’t update the cell’s content properly when reusing, you may see artifacts like image flickering.
- Asynchronous Image Loading: In many cases, images for table view or collection view cells are loaded asynchronously from a remote source, such as the web or a network request. Asynchronous loading ensures that image loading doesn’t block the UI, but it can introduce this flickering issue if not handled correctly.
To solve the image flickering issue, you can follow these approaches:
Prepare for Reuse: Override the prepareForReuse() method in your table view or collection view cell subclass and reset the cell’s content to its initial state. This ensures that old content is cleared before the cell is reused.
It is recommended to cancel ongoing image requests when a cell is being prepared for reuse or when it’s about to be dequeued for reuse. This ensures that unnecessary image downloads are stopped, preventing the display of incorrect or outdated images in reused cells.
Typically, you would cancel the image requests in the prepareForReuse() method of your table view or collection view cell subclass. This method is called just before the cell is reused, allowing you to reset the cell’s content and cancel any ongoing image requests.
Here’s an example of how you can cancel image requests in a table view cell:
import UIKit
import Combine
final class FeedItemRowViewCell: UITableViewCell, ReusableView {
private var viewModel: FeedItemViewModel?
private var cancelables = Set<AnyCancellable>()
// MARK: - UI Properties
private lazy var imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleToFill
imageView.layer.cornerRadius = 10
imageView.clipsToBounds = true
return imageView
}()
// Reset the cell's content and cancel the image request
override func prepareForReuse() {
super.prepareForReuse()
// Reset the image view's content
imageView.image = nil
// Cancel the ongoing image request
viewModel?.onReuse()
cancelables.forEach { $0.cancel() }
cancelables.removeAll()
}
}
// MARK: - Configuration
extension FeedItemRowViewCell {
// Configure the cell with an image URL
func configure(with viewModel: FeedItemViewModel) {
self.viewModel = viewModel
// Cancel any ongoing image request before starting a new one
viewModel?.onReuse()
titleLabel.text = viewModel.title
overviewLabel.text = viewModel.overview
imageView.image = viewModel.image
// Start a new image request
viewModel
.$image
.receive(on: DispatchQueue.main)
.sink { [weak self] image in
self?.imageView.image = image
}.store(in: &cancelables)
viewModel.fetchImage()
}
}
- Cancel Ongoing Image Requests: If you’re loading images asynchronously, make sure to cancel any ongoing image requests for a cell that’s about to be reused. This prevents the previous image from appearing momentarily before the new image is loaded.
- Set Placeholder or Clear Image: When reusing a cell, set a placeholder image or clear the image view’s contents temporarily until the new image is loaded. This prevents the previous image from being displayed until the new image is ready.
- Cache Images: Implement an image caching mechanism to cache the downloaded images. This way, when a cell is reused, you can check if the image is already cached before loading it again. This avoids unnecessary flickering when the same image is used in multiple cells.
By properly managing cell reuse, canceling ongoing image requests, setting placeholders, and caching images, you can eliminate or significantly reduce the image flickering issue when scrolling rapidly in table views and collection views.
References
https://developer.apple.com/videos/play/wwdc2019/712/
https://developer.apple.com/documentation/uikit/uitableview/1614891-dequeuereusablecell
Leave a comment