Swift - 如何在UICollectionview中滚动期间停止图像更改?

时间:2018-04-07 10:05:18

标签: ios swift uicollectionview

我使用collectionview显示下载的网址识别图像。这是我的代码:

var urlArray = ["url1", "url2", ..., "urln"]

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
       let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell, for: indexPath) as? MyCell
       cell.objectImageView.downloadedFrom(link: urlArray[indexPath.row], contentMode: UIViewContentMode.scaleAspectFit)
}

这是我的异步下载图片的扩展程序代码:

func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
    contentMode = mode
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard
            let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
            let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
            let data = data, error == nil,
            let image = UIImage(data: data)
            else { return }
        DispatchQueue.main.async() {
            self.image = image
        }
        }.resume()
}

func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
    guard let url = URL(string: link) else { return }
    downloadedFrom(url: url, contentMode: mode)
}

当我滚动collectionview时,每个单元格中的图像会不断变化。我已尝试通过在imageview方法加载之前将nil设置为cellForItemAt来解决此问题:

cell.objectImageView.image = nil
cell.objectImageView.downloadedFrom(link: urlArray[indexPath.row], contentMode: UIViewContentMode.scaleAspectFit)

但它不起作用。如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

设置nil无济于事。它主要是细胞再利用问题。

图像下载是异步的,这意味着它将在以后完成 如果您滚动并在图像下载完成之前重新使用cell,它将设置上一个下载调用的图像,从而引起您的连续更改,这绝对是一个小故障。

如果完成的下载任务是针对imageView或已被重复使用,您将需要其他信息来跟踪此情况,以便基本确定是否已经重新使用,在这种情况下,另一个下载任务将设置它。

我们可以通过多种方式执行此操作,但在下文中,我们会检查下载开始时的网址是否与下载完成时的网址相同。

简单的事情:

func downloadedFrom(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
    /*
     1.
     strUniqueIdentifier_Initial will be the url that caused the download to start.
     A copy of this will be accessible in the closure later.

     Also, we bind this to the imageView for case handling in the closure.
     */
    let strUniqueIdentifier_Initial = url.absoluteString
    self.accessibilityLabel = strUniqueIdentifier_Initial

    contentMode = mode
    let dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
        guard
            let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
            let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
            let data = data, error == nil,
            let image = UIImage(data: data)
            else { return }

        /*
         2.
         strUniqueIdentifier_Initial is a copy of the url from the start of the function

         strUniqueIdentifier_Current is the url of the current imageView as we use self
          so if the imageView is reused, this method will be called on it again and at
          that time it it's binded url string will be for the latest download call

         If there's mismatch then the imageView was reused
         */
        let strUniqueIdentifier_Current = self.accessibilityLabel
        if strUniqueIdentifier_Initial != strUniqueIdentifier_Current {
            //previous download task so ignore
            return
        }

        DispatchQueue.main.async() {
            self.image = image
        }
    }
    dataTask.resume()
}

您可以优化逻辑以取消先前的下载,但这是解决核心问题的基本解决方案。