I have a UICollectionView that displays images in a grid, but when scrolling quickly, it displays the wrong image in the cell instantly until the image is loaded from the S3 repository and then the correct image is displayed.
I saw questions and answers related to this issue on SO, but none of the solutions work for me. The let items = [[String: Any]]() dictionary is populated after an API call. I need a cell to remove an image from a recycled cell. Now there is an unpleasant effect of the "dance" of the image.
Here is my code:
var searchResults = [[String: Any]]() let cellId = "cellId" override func viewDidLoad() { super.viewDidLoad() collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout) collectionView.delegate = self collectionView.dataSource = self collectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: cellId) func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MyCollectionViewCell let item = searchResults[indexPath.item] cell.backgroundColor = .white cell.itemLabel.text = item["title"] as? String let imageUrl = item["img_url"] as! String let url = URL(string: imageUrl) let request = Request(url: url!) cell.itemImageView.image = nil Nuke.loadImage(with: request, into: cell.itemImageView) return cell } class MyCollectionViewCell: UICollectionViewCell { var itemImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit imageView.layer.cornerRadius = 0 imageView.clipsToBounds = true imageView.translatesAutoresizingMaskIntoConstraints = false imageView.backgroundColor = .white return imageView }() override init(frame: CGRect) { super.init(frame: frame) --------- } override func prepareForReuse() { super.prepareForReuse() itemImageView.image = nil } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
I changed the image loading per cell with the following code:
cell.itemImageView.image = nil APIManager.sharedInstance.myImageQuery(url: imageUrl) { (image) in guard let cell = collectionView.cellForItem(at: indexPath) as? MyCollectionViewCell else { return } cell.itemImageView.image = image cell.activityIndicator.stopAnimating() }
Here is my API manager.
struct APIManager { static let sharedInstance = APIManager() func myImageQuery(url: String, completionHandler: @escaping (UIImage?) -> ()) { if let url = URL(string: url) { Manager.shared.loadImage(with: url, token: nil) {
If the user scrolls past the content limit, more items will be loaded in my collection view. This seems to be the root of the problem when cell reuse is reused. Other fields in the cell, such as the name of the item, are also replaced when new items are loaded.
func scrollViewDidScroll(_ scrollView: UIScrollView) { if (scrollView.contentOffset.y + view.frame.size.height) > (scrollView.contentSize.height * 0.8) { loadItems() } }
Here is my data upload function
func loadItems() { if loadingMoreItems { return } if noMoreData { return } if(!Utility.isConnectedToNetwork()){ return } loadingMoreItems = true currentPage = currentPage + 1 LoadingOverlay.shared.showOverlay(view: self.view) APIManager.sharedInstance.getItems(itemURL, page: currentPage) { (result, error) -> () in if error != nil { } else { self.parseData(jsonData: result!) } self.loadingMoreItems = false LoadingOverlay.shared.hideOverlayView() } } func parseData(jsonData: [String: Any]) { guard let items = jsonData["items"] as? [[String: Any]] else { return } if items.count == 0 { noMoreData = true return } for item in items { searchResults.append(item) } for index in 0..<self.searchResults.count { let url = URL(string: self.searchResults[index]["img_url"] as! String) let request = Request(url: url!) self.preHeatedImages.append(request) } self.preheater.startPreheating(with: self.preHeatedImages) DispatchQueue.main.async { // appendCollectionView(numberOfItems: items.count) self.collectionView.reloadData() self.collectionView.collectionViewLayout.invalidateLayout() } }
ios swift3 uicollectionview uicollectionviewcell
markhorrocks
source share