【问题标题】:Image on CollectionView cell changes when scrolling滚动时 CollectionView 单元格上的图像发生变化
【发布时间】:2021-09-21 02:44:38
【问题描述】:

我有一个包含自定义单元格的 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
                    }
                }
            }
        }
    }

【问题讨论】:

    标签: ios swift uicollectionview


    【解决方案1】:

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

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

    另一方面,cell 是 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
            }
        }
    }
    

    【讨论】:

    • 我试过了,但效果不好。我更新了问题,以便您可以看到原始的加载功能。
    【解决方案2】:

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

    override func prepareForReuse() {
        super.prepareForReuse()
        imageView?.image = nil
    }
    

    【讨论】:

    • 这样做是重新下载每个单元格的图像,有时我仍然遇到问题。
    猜你喜欢
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 2018-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多