Search code examples
iosswiftimagesdwebimage

SDWebImage's sd_setImageWithURL updates the wrong cell with Image on scrolling!! Is that a expected behaviour?


OverView

I am downloading the images from web server using SDWebImage in my collectionView cell.

if floor.hasTitleImage != nil {
            self.floorImageView.sd_setImageWithURL(NSURL(string:(floor.hasTitleImage?.imageURL)!), placeholderImage: UIImage(named: "Placeholder"), completed: { (imageDownloaded, error, cacheType, url) in
                if imageDownloaded.size.width > 300 {
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
                        let resizedImage = imageDownloaded.resizePreservingAspectRatio(toSize: CGSize(width: 300, height: 300))
                        SDImageCache.sharedImageCache().removeImageForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, fromDisk: true, withCompletion: {
                            SDImageCache.sharedImageCache().storeImage(resizedImage, forKey: NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, toDisk: true)
                        })
                    })
                }
            })
        }
        else{
            self.floorImageView.image = UIImage(named: "Placeholder")
        }

Now what this code is doing is downloading the image using the url stored in core data element called floor. Now the images downloaded are way bigger then my ImageView so am resizing it to somewhere around 300px.

I know that SDWebImage does intense caching by default and uses url's absolute string as key for its cache. So Once resized! I replace the original image with my resized image.

Issue

Everything worked fine as expected! till I tested it with low internet speed which caused the image download to be delayed.

If the image downloading takes time, till then if I scroll the tableView and the cell gets reused, once the image downloaded SDWebImage loads the imageView of the cell but ends up resulting the wrong cell with wrong image.

Though this happens only once as for next time onwards image will be loaded from cache everything works fine. But Is this the expected behaviour or am I doing something wrong???

Solutions I could think of :

  1. As sd_setImageWithURL by default sets the value of imageView with downloaded image rather than downloading the image using sd_setImageWithURL, if I can download it using,

    if floor.hasTitleImage != nil {
        if let downloadedImage = SDImageCache.sharedImageCache().imageFromMemoryCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString){
            self.floorImageView.image = downloadedImage
        }
        else if let diskImage = SDImageCache.sharedImageCache().imageFromDiskCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString){
            self.floorImageView.image = diskImage
        }
        else{
            let downloadManager = SDWebImageManager.sharedManager();
            downloadManager.downloadImageWithURL(NSURL(string:(floor.hasTitleImage?.imageURL)!), options: [], progress: nil, completed: { (downloadedImage, error, cacheType, finished, url) in
                if downloadedImage.size.width > 300 {
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
                        let resizedImage = downloadedImage.resizePreservingAspectRatio(toSize: CGSize(width: 300, height: 300))
                        SDImageCache.sharedImageCache().removeImageForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, fromDisk: true, withCompletion: {
                            SDImageCache.sharedImageCache().storeImage(resizedImage, forKey: NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString, toDisk: true)
                        })
                        if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage?.imageURL! == url{
                            dispatch_async(dispatch_get_main_queue(), {
                                self.floorImageView.image = SDImageCache.sharedImageCache().imageFromMemoryCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString)
                                self.reloadInputViews()
                            })
                        }
                    })
                }
            })
        }
    }
    else{
        self.floorImageView.image = UIImage(named: "Placeholder")
    }
    

Though it works it will load the image only when I scroll the tableView :(

Questions :

  1. Is what sd_setImageWithURL doing is expected behaviour ?? I mean every single one of the app using SDWebImage with tableView or CollectionView must have faced this correct ?? So if no body faced it I must be doing something wrong :( Am I doing anything wrong ?

  2. Is what am doing using SDWebImageManager.sharedManager is correct ??? Is that long code required? Checking memory cache then disk cache and finally making call to download image isn't it too much :o

  3. Why is that my second approach not loading the image once downloaded and expects the collection view to scroll and reload the cell to display the image ??

EDIT

I solved the 3rd question :) Sorry my mistake. I was comparing if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage?.imageURL! == url{ which is wrong because url above is NSURL where as floor.hasTitleImage?.imageURL! is NSString.

So Updated solution :

if floor.hasTitleImage?.imageURL != nil && floor.hasTitleImage!.imageURL! == url.absoluteString{
                                dispatch_async(dispatch_get_main_queue(), {
                                    self.floorImageView.image = SDImageCache.sharedImageCache().imageFromDiskCacheForKey(NSURL(string:(floor.hasTitleImage?.imageURL)!)?.absoluteString)
                                    self.setNeedsLayout()
                                })
                            }

Though my second approach downloads the image now and updates the cell properly but now image downloading is very slow compared to sd_setImageWithURL.

Is there any way I can use the method1 sd_setImageWithURL and get the image updated properly?

Please help me!! what am doing wrong !! Thanks in advance :)


Solution

    1. Is what sd_setImageWithURL doing is expected behaviour ?? I mean every single one of the app using SDWebImage with tableView or CollectionView must have faced this correct ?? So if no body faced it I must be doing something wrong :( Am I doing anything wrong ?

    No, it's not expected behaviour. I'm using SDWEbImage as well both tableView and collectionView and both are work fine.

    1. Is what am doing using SDWebImageManager.sharedManager is correct ??? Is that long code required? Checking memory cache then disk cache and finally making call to download image isn't it too much :o

    You don't have to cache image youself, SDWebImage take care caching process for you (in memory and disk).

    If you need to resize image before cache and display, I think better do it by implement SDWebImageManagerDelegate which allows you to transform the image after it has been downloaded but before cache and display.

    UPDATE:

    If you use sd_setImageWithURL, there is another solution to prevent wrong assign image is by override prepareForReuse() method in UICollectionViewCell subclass and call sd_cancelCurrentImageLoad() on imageView and set image to nil.

    hope this help.