import UIKit class GIFCollectionViewCell: UICollectionViewCell { private let gifPlayerView: GIFPlayerView = { let player = GIFPlayerView() player.layer.cornerRadius = 8 player.clipsToBounds = true return player }() private let loadingIndicator: UIActivityIndicatorView = { let indicator = UIActivityIndicatorView(style: .medium) indicator.hidesWhenStopped = true return indicator }() private let placeholderLabel: UILabel = { let label = UILabel() label.text = "GIF" label.textAlignment = .center label.font = .systemFont(ofSize: 12, weight: .medium) label.textColor = .systemGray return label }() private var currentTask: URLSessionDataTask? override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } override func prepareForReuse() { super.prepareForReuse() currentTask?.cancel() currentTask = nil gifPlayerView.stopAnimating() placeholderLabel.isHidden = false loadingIndicator.stopAnimating() } private func setupUI() { contentView.addSubview(gifPlayerView) contentView.addSubview(loadingIndicator) contentView.addSubview(placeholderLabel) gifPlayerView.translatesAutoresizingMaskIntoConstraints = false loadingIndicator.translatesAutoresizingMaskIntoConstraints = false placeholderLabel.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ gifPlayerView.topAnchor.constraint(equalTo: contentView.topAnchor), gifPlayerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), gifPlayerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), gifPlayerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), loadingIndicator.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), loadingIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), placeholderLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), placeholderLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), ]) contentView.layer.cornerRadius = 8 contentView.layer.borderWidth = 1 contentView.layer.borderColor = UIColor.systemGray4.cgColor } func configure(with urlString: String) { // Cancel any existing task currentTask?.cancel() // Reset UI gifPlayerView.stopAnimating() placeholderLabel.isHidden = false loadingIndicator.startAnimating() guard let url = URL(string: urlString) else { loadingIndicator.stopAnimating() return } // Load the GIF currentTask = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in DispatchQueue.main.async { guard let self = self else { return } self.loadingIndicator.stopAnimating() if let data = data, error == nil { self.gifPlayerView.loadGIF(from: data) self.gifPlayerView.startAnimating() self.placeholderLabel.isHidden = true } else { self.placeholderLabel.isHidden = false } } } currentTask?.resume() } }