7

When I'm populating new data in my tableView Controller, my top cell is being rewrite by the bottom one. Let me explain. I have one image that is loading async in the latest cell row, in the bottom. All the other cells are loading static with an image that is in the app. When I scroll down my app and display my bottom cell which have a different image than the others cells, and I scroll up again, I see the first shop image has changed to the dynamic one loaded in the bottom cell. Does anyone know why this is happening?

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell : shopsTableViewCell = tableView.dequeueReusableCellWithIdentifier("shopCell", forIndexPath: indexPath) as! shopsTableViewCell

    cell.index = indexPath.row
    let qualityOfServiceClass = QOS_CLASS_BACKGROUND
    let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
    let apiCall = webApi()


        dispatch_async(backgroundQueue, {
            apiCall.downloadPhotoAsync("http://api-ytyg.wbbdev.com/files/shops/\(shops[indexPath.row].id)/\(shops[indexPath.row].featured_img)"){(image: UIImage?) -> Void in
                dispatch_async(dispatch_get_main_queue()){
                    if(image != nil){
                        if (cell.index == indexPath.row){
    //                            shops[indexPath.row].photo = image!
                            cell.shopImg?.image = image
                        }

                    }else{
                        shops[indexPath.row].photo = UIImage.init(named: "No_Image_Available.jpg")!
                    }
                }
            }
        })
    cell.shopName?.text = shops[indexPath.row].name
    cell.shopDescription?.text = shops[indexPath.row].address

    cell.label1?.text = shops[indexPath.row].city + " | " + shops[indexPath.row].distance + "Km"
    cell.becomeFirstResponder()
    return cell
}

enter image description here

enter image description here

enter image description here

Alfro
  • 1,524
  • 2
  • 21
  • 37
  • show your declaration of shopImg and shopName, shopDescription – Akshansh Thakur Jun 30 '16 at 19:58
  • edited to add custom cell class were is the declaration of the properties you requested – Alfro Jun 30 '16 at 20:00
  • check my answer.. and why don't you use SDWebImage library, it is very efficient compared to all this. it will process faster with cache and everything – Akshansh Thakur Jun 30 '16 at 20:19
  • I don't know this library. I wish had known soon. Now it's been a while since I'm investigating and arriving at the end soon – Alfro Jun 30 '16 at 20:21
  • I have added code using this library, check. – Akshansh Thakur Jun 30 '16 at 20:25
  • sorry but while investigating and trying new things I've found that the outlet is causing the app crashes, and only when the image has been downloaded from the server. I have edited my post to show new captures and code – Alfro Jun 30 '16 at 20:28

2 Answers2

13

it is because your cells are being reused :

// this line initialize a cell of type shopsTableViewCell 
//if no cell can be reused, get an already used cell else
let cell : shopsTableViewCell 
                 = tableView.dequeueReusableCellWithIdentifier("shopCell",
                                  forIndexPath: indexPath) as! shopsTableViewCell

so image and other data should be the last image and data of the dequeued cell.

To avoid this, you shall implement prepareForReuse in the cell class file to reset data and image. docs of apple about prepareForReuse func

From Apple docs :

If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.

 //Example :
 override func prepareForReuse() {
    super.prepareForReuse()
    //Do reset here
    this.shopImg?.image = nil
    this.shopName?.text = ""
    this.shopDescription?.text = ""

}
LHIOUI
  • 3,287
  • 2
  • 24
  • 37
0

I think the issue is that while you are scrolling, your images are still in downloading process (that's why you have a nil on that particular line of code). So you have to wait for it.

I am guessing that you have an array of images. If you don't, then you must make one. You must use it like so:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return arrayThatcontainsShopImages.count
}

Note: that every time you add downloaded image to this array, you should call tableView.reloadData()

Using SDWebImage can be far superior. Look at the following code using this library, you just create an instance of UIImageView. then using this library, you can download image directy to imageview instance, and to access the image, you can use imageView.image property of UIImageView class

 let imageView = UIImageView()
 imageView.sd_setImageWithURL(someURL, placeholderImage: UIImage())
 self.arrayOfImageViews.append(imageView)
Akshansh Thakur
  • 5,163
  • 2
  • 21
  • 38