fandro
fandro

Reputation: 4903

Best way to load image url swift 2 in UITableView

I want to create A Ui TableView with a list of image link with swift 2:

for example : var images = ["link1","link2",...,linkN"]

I create a custom cell to display the image :

   let cell = tableView.dequeueReusableCellWithIdentifier(CurrentFormTableView.CellIdentifiers.ImageCell, forIndexPath: indexPath) as! ImageCell
            cell.urlImageView.tag = indexPath.row
            cell.displayImage(images[index.row])
            cell.selectionStyle = UITableViewCellSelectionStyle.None

            return cell

And here I have my custom cell to load my image :

import UIKit

class ImageCell: UITableViewCell {

    @IBOutlet weak var urlImageView: UIImageView!
    @IBOutlet weak var loadingStatus: UIActivityIndicatorView!

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

    func loadImageFromUrl(url: String, view: UIImageView){
        if view.image == nil {

            self.startLoading()
            // Create Url from string
            let url = NSURL(string: url)!


            // Download task:
            // - sharedSession = global NSURLCache, NSHTTPCookieStorage and NSURLCredentialStorage objects.
            let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
                // if responseData is not null...
                if let data = responseData{

                    // execute in UI thread
                    dispatch_async(dispatch_get_main_queue(), { () -> Void in
                        self.stopLoading()
                        view.image = UIImage(data: data)
                    })
                }
            }

            // Run task
            task.resume()
        } 
    }

    func displayImage(imageUrl: String){

        imageView?.image = nil
        if imageUrl != nil && imageUrl != "" {
            print(imageUrl)

                loadImageFromUrl(imageUrl,view: urlImageView)

        } else {
            loadingStatus.hidden = true
        }
    }

    func startLoading(){
        loadingStatus.hidden = false
        loadingStatus.startAnimating()
    }

    func stopLoading(){
        loadingStatus.hidden = true
        loadingStatus.stopAnimating()
    }
}

The problem is that, she times the images are loading correctly, and sometimes, one image or more "override the other" so I have multiple identical images instead of see all my different images. How it is possible ? Where is my mistake ?

Upvotes: 2

Views: 7045

Answers (1)

max_
max_

Reputation: 24521

You're not implementing the reuse of the cells, meaning that the imageView's image of one cell will be the same as another that it was reused from, until the new image has loaded.

To prevent this, implement the -prepareForReuse: method:

override func prepareForReuse() {
    super.prepareForReuse()
    urlImageView.image = nil
    // cancel loading
}

Furthermore, you shouldn't be doing any network-related code in the view layer, it should be done in the view controller. This will allow you to implement caching mechanisms if the image has already been downloaded for a specific cell, as well as alter the state of other views.

i.e. In your view controller:

var cachedImages = [String: UIImage]()    

func cellForRow...() {
    let imageURL = imageURLs[indexPath.row]
    if let image = cachedImages[imageURL] {
        cell.urlImageView.image = cachedImages[imageURL]
    }
    else {
        downloadImage(indexPath, { image in
            if let image = image {
                cachedImages[imageURL] = image
                cell.urlImageView.image = image
            }
        })
    }
}

func downloadImage(indexPath: NSIndexPath, callback: () -> (UIImage?)) {
    let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (responseData, responseUrl, error) -> Void in
            // if responseData is not null...
            if let data = responseData {
                // execute in UI thread
                dispatch_async(dispatch_get_main_queue(), {
                    callback(UIImage(data: data))
                })
            }
            else {
                dispatch_async(dispatch_get_main_queue(), {
                    callback(nil)
                })
            }
        }

        // Run task
        task.resume()
}

Upvotes: 6

Related Questions