异步图像每次滚动时都会发生变化



所以我正在创建一个iOS应用程序,让你浏览Unsplash壁纸,我使用UICollectionView在单元格中加载图像,但每当我滚动图像时,我都会返回图像更改为不同的图像。

这是代码

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
    let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
    dispatch_async(downloadQueue) {
        let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
        let imageData = NSData(contentsOfURL: imageURL!)
        var image: UIImage?
        if imageData != nil {
            image = UIImage(data: imageData!)
        }
        dispatch_async(dispatch_get_main_queue()) {
            cell.imageView.image = image
        }
    }
    return cell
}

EDIT:发生了两件事:

  1. collectionView.dequeueReusableCellWithReuseIdentifier重用已创建的单元格(如果有可用的单元格)。因此,您正在对以前的某个单元格进行排队。

  2. 加载图像的URL每次调用时都会生成一个随机图像。

因此,当您滚动到集合视图的第一行不在屏幕上时,这些单元格就会被重用。然后,当您向上滚动时,将使用"https://unsplash.it/200/300/?random" 中的新随机图像重新创建单元格

规避这种情况的一种方法是根据单元格索引保留所有图像的索引数组。当然,如果你的图片很大和/或你有一个很大的收藏View,你可能会耗尽内存。

看看我模拟的这个代码。我还没有验证代码是否真的有效。

//instance var to store your images
var imageArray: [UIImage]?
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
    // Check if we have already loaded an image for this cell index
    if let oldImage: UIImage = imageArray[indexPath.row] {
        cell.imageView.image = oldImage
        return cell
    } else {
        // remove the old image, before downloading the new one
        cell.imageView.image = nil
    }

    let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
    dispatch_async(downloadQueue) {
        let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
        let imageData = NSData(contentsOfURL: imageURL!)
        var image: UIImage?
        if imageData != nil {
            image = UIImage(data: imageData!)
            // Save image in array so we can access later
            imageArray.insert(image, atIndex: indexPath.row)
        }
        dispatch_async(dispatch_get_main_queue()) {
            cell.imageView.image = image
        }
    }
    return cell
}

@toddg解决方案是正确的。但它在重复使用细胞方面仍然存在问题。

如果该小区在网络呼叫完成之前被重用,则它将把下载的图像分配给另一个小区。

所以我更改了代码如下。

var imageArray: [UIImage]?
let downloadQueue = dispatch_queue_create("com.donbytyqi.Papers", nil)
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
    if let oldImage: UIImage = imageArray[indexPath.row] {
        cell.imageView.image = oldImage
        return cell
    } else {
        cell.imageView.image = nil;
        downloadImage(indexPath);
    }

    return cell
}
func downloadImage(indexPath: NSIndexPath) {
    dispatch_async(downloadQueue) {
        let imageURL = NSURL(string: "https://unsplash.it/200/300/?random")
        let imageData = NSData(contentsOfURL: imageURL!)
        var image: UIImage?
        if imageData != nil {
            image = UIImage(data: imageData!)
        }
        let cell = self.collectionView .cellForItemAtIndexPath(indexPath) as! ImageCollectionViewCell
        dispatch_async(dispatch_get_main_queue()) {
            cell.imageView.image = image
        }
    }
}

希望这能有所帮助。

让我解释一下实际发生了什么
当您滚动并返回时,您实际上会看到以前显示的单元格和以前下载的图像(因为dequeueReusableCellWithReuseIdentifier:),并且您将一直看到该图像,直到您的新图像不会下载,即直到执行cell.imageView.image = image行。

因此,您必须执行以下操作:

  1. dequeueReusableCellWithReuseIdentifier:行之后设置cell.imageView.image = nil,如下所示:

    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! ImageCollectionViewCell
    cell.imageView.image = nil;
    //...  
    

    这将从imageView中删除以前下载的图像,直到下载新图像。

  2. 你应该使用SDWebImage或UIImageView+AFNetworking之类的东西来进行异步图像下载,并支持缓存,因为每次调用你的方法时,图像都会被一次又一次地下载,而不是获得缓存的图像,这是对流量的浪费。

祝你好运!

最新更新