滚动时 CollectionView 单元格上的图像会发生变化

时间:2021-07-11 18:01:09

标签: ios swift uicollectionview

我有一个包含自定义单元格的 collectionView。每个自定义单元格中都有一个图像和其他组件。我遇到的问题是,当我滚动时,图像从 collectionView 上的其他单元格更改为另一个图像。我将放置我正在使用的代码,以便更清楚地了解我是如何实现 collectionView 及其单元格的。

func registerCell() {
        collectionView.register(PageCell.self, forCellWithReuseIdentifier: cellId)
    }
    
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        
        let pageNumber = Int(targetContentOffset.pointee.x / view.frame.width)
        pageControl.currentPage = pageNumber
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        return feedTitles.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! PageCell

        cell.titleLabel.text = self.feedTitles[indexPath.item].uppercased()
        cell.textView.text = self.feedDescription[indexPath.item]

        guard let imageURL = URL(string: self.feedImages[indexPath.item]) else {return cell}
        cell.imageView.load(url: imageURL)
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: collectionView.frame.height)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

这是从服务器获取图像的代码:

var feeds: [Feed]?
    func getFeed(){
        feedService.getFeed() { [weak self](feedRes) in
            switch feedRes {
            case .success(let successResponse):
                print("Success Feed")
                if successResponse.data.count < 1 {
                } else{
                    self?.feeds = successResponse.data
                    self?.feedTitles = self?.feeds?.compactMap({ $0.title
                    }) ?? ["Titulo Noticia"]
                    self?.feedImages = self?.feeds?.compactMap({ $0.image
                    }) ?? ["Imagen"]
                    self?.feedDescription = self?.feeds?.compactMap({ $0.content
                    }) ?? ["Descripcion Imagen"]
                    self?.collectionView.reloadData()
                    self?.pageControl.numberOfPages = (self?.feedTitles.count)!
                    self?.pageControl.isHidden = false
                }
            case .failure(let feedError):
                print("Fail Feed \(feedError)")
                print(feedError.localizedDescription)
                self?.pageControl.isHidden = true
            }
        }
    }

这是“加载”函数:

func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

问题出在您的 cell.imageView.load(url: imageURL) 实现中。

下载图片是一个异步操作。当您创建一个单元格时 - 您开始了一个操作,但您不知道它何时会完成。每当它完成时,它就会在您的 UIImageView 实例上设置图像。

另一方面,单元格是 collectionView 的可重用组件,当您滚动时,从顶部离开屏幕而消失的相同单元格实例也会通过从屏幕底部进入而重新出现。这意味着一个 cell 实例可以触发多个图像下载请求,并且不会取消它之前的动态图像下载请求。它也不知道完成了哪个图像下载调用 - 最后一个(或之前的一个等等)

您需要做的是 - 在 imageView 加载方法中添加一些额外的保护,以便当一个单元格(以及 imageView)实例被重用时 - 它只为最后一个请求设置图像(而不是在最后一个请求之前完成的那些)一个)。

将以下代码复制粘贴到您的项目中。

import UIKit
import ObjectiveC.runtime

private var keyTagIdentifier: String?

public extension UIView {
    var tagIdentifier: String? {
        get { return objc_getAssociatedObject(self, &keyTagIdentifier) as? String }
        set { objc_setAssociatedObject(self, &keyTagIdentifier, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }
}

像这样更新你的图片加载扩展

public extension UIImageView {
    func load(url: URL) {
        // 1. Assign the current request identifier on this instance
        let lastRequestIdentifier = url.absoluteString
        self.tagIdentifier = lastRequestIdentifier
        
        // 2. Download the image as you are doing today

        // 3. After the image has been downloaded and you set the image
        // Please check whether we are still interested in same image or not
        // This will be written inside your image download completion block
        if self.tagIdentifier == lastRequestIdentifier {
            self.image = // the image you downloaded
        }
    }
}

答案 1 :(得分:0)

我认为问题是由于 UICollectionView 重复使用单元格造成的。这意味着从顶部消失的单元格在底部呈现,通常与从顶部消失的单元格具有相同的状态,反之亦然。您可以通过将以下代码添加到您的 PageCell,

来避免这种行为
override func prepareForReuse() {
    super.prepareForReuse()
    imageView?.image = nil
}