Tae Chatnaratanakun
Tae Chatnaratanakun

Reputation: 157

UITableView - cell images changing while scrolling

Hi my problem is that when I scroll TableView the image will appear in a wrong cell, after a few seconds the correct image appears.

here is my code

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}

[cell setOpaque:NO];
[cell setBackgroundColor: [UIColor clearColor]];

PlaceData *data = [tableData objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
UILabel *sciNameLabel = (UILabel *)[cell viewWithTag:200];
UIImageView *thumbnailImageView = (UIImageView *)[cell viewWithTag:300];
nameLabel.text = data.name;
sciNameLabel.text = data.scientific_name;


// get a dispatch queue
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
    NSURL *urlToPicture = [NSURL URLWithString:[NSString stringWithFormat:@"%@", data.thumbnail]];
    NSData *imgData = [NSData dataWithContentsOfURL:urlToPicture options:0 error:nil];

    // This will set the image when loading is finished
    dispatch_async(dispatch_get_main_queue(), ^{
        UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
        thumbnailImageView.image = tmpImage;
        //dispatch_release(concurrentQueue);
         });
     });

       return cell;
 }

please help me

Upvotes: 9

Views: 9381

Answers (6)

Muhammad Zeshan Mazhar
Muhammad Zeshan Mazhar

Reputation: 445

cell.thumbnailimages.image=nil

cell.thumbnailimages.setImageWith(imageurl!)

I think these two lines solve your problem.

Upvotes: 0

Brahmaiah Thota
Brahmaiah Thota

Reputation: 11

Swift 3.0

DispatchQueue.main.async(execute: {() -> Void in

    if cell.tag == indexPath.row {
        var tmpImage = UIImage(data: imgData)
        thumbnailImageView.image = tmpImage
    }
})

Upvotes: 1

Gaurav Srivastava
Gaurav Srivastava

Reputation: 514

You can try adding following code to your cellForRowAtIndexPath -

1) Assign an index value to your custom cell. For instance,

cell.tag = indexPath.row

2) On main thread, before assigning the image, check if the image belongs the corresponding cell by matching it with the tag.

dispatch_async(dispatch_get_main_queue(), ^{
    if(cell.tag == indexPath.row) {
      UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
      thumbnailImageView.image = tmpImage;
    }});
 });

Upvotes: 22

Frantz Romain
Frantz Romain

Reputation: 846

I would do all my data binding at - tableView:willDisplayCell:forRowAtIndexPath: only because at cellForRowAtIndexPath your cell hasn't been drawn yet. Another solution you can use is AFNetworking like someone else mentioned before me.

Upvotes: 1

Saheb Roy
Saheb Roy

Reputation: 5957

As becauase your ImageView is being loaded in an async dispatch call which is NOT on the main thread and is being called in some other thread so there is a delay in fetching the data from the URL and then converting it to an UIImage. THis process takes a bit of time as you know but you are scrolling the tableview in a faster rate. And as you know cellForRowAtIndexPath reuses any cell that is out of the window so the cell that is being reused might NOT fetched the imagedata that it WAS TO RETRIEVE previously when it was in the Window. Thus it loads the wrong data and then again when async is fired for that specific cell the cell loads that image but there comes the delay.

To overcome this feature as Chronch pointed it out u can leave the imageview as nil OR you can use AFNetworking's own UIImageView catagory which has a superb class to load imageview images quite elegantly

I'll leave u a link to it AFNetworking

Upvotes: 3

Rony Rozen
Rony Rozen

Reputation: 4047

You are reusing old cells, which is a good thing. However, you are not initializing the image in the cell's image view, which is not such a good thing. What you're describing happens because an old cell, with an image that was already loaded for that cell, is used for the new cell. You are then loading that cell's image in the background (which, again, is good) but it takes a few moments for that image to fully load. In the meantime, the image that was already loaded on the old cell, is displayed (and that's the reason you're seeing a wrong image in that cell, for a few moments).

The solution? add either

thumbnailImageView.image = nil

or

thumbnailImageView.image = someDefaultImageWhileYourRealOneLoads

right before dispatch_queue_t concurrentQueue ....

That way, you won't see the old (irrelevant) image while the real one loads.

I hope this helps. Good luck.

Upvotes: 6

Related Questions