Fernando Soares
Fernando Soares

Reputation: 59

UITabelView changed Image in scrolling

Every time I scroll the TableView, my images gets messed up, mainly the first row. I realy don`t kwon what to do.

Here is the code:

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";

    BarCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    [cell.activityFotoBar startAnimating];
    cell.activityFotoBar.hidesWhenStopped = YES;

   if(!cell){
       cell = [[BarCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
   }

    NSMutableDictionary *infoBar = [self.bares objectAtIndex:indexPath.row];
    NSString *nomeImagem = [infoBar objectForKey:@"foto"];

    NSURL *url = [NSURL URLWithString:nomeImagem];
    NSURLRequest *requestImagem = [NSURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:requestImagem queue:[NSOperationQueue currentQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {

        if(connectionError == nil){
            cell.imageViewImagemBar.image = [UIImage imageWithData:data];
            [cell.activityFotoBar stopAnimating];
        }

    }];


    cell.labelNomeBar.text = [infoBar objectForKey:@"nome"];
    cell.labelEnderecoBar.text = [infoBar objectForKey:@"endereco"];
    cell.labelAvaliacaoBar.text = [NSString stringWithFormat:@"Votos: %@", [infoBar objectForKey:@"votos"]];

    return cell;

}

Thanks in advance!

Upvotes: 0

Views: 175

Answers (3)

CouchDeveloper
CouchDeveloper

Reputation: 19114

The code should "almost" work. You just need to fix one issue to get it working (though not optimally):

When the asynchronous request sendAsynchronousRequest:queue:completionHandler: has been finished, the completion handler will be called. Then, you need to retrieve the cell again from the table view specifying the indexPath at the time the request has been started.

You just need to capture the indexPath within the block in order to get a reference that stays valid until after the block finishes.

The UITableView's method to retrieve the cell is

- (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath

If the return value (a cell) is nil the cell at index path indexPath is not visible.

So, the completion block will look as follows:

    if (error == nil && ... ) {
        BarCell* cell = [self.tableView cellForRowAtIndexPath:indexPath]
        if (cell) {
            cell.imageViewImagemBar.image = [UIImage imageWithData:data];
            [cell.activityFotoBar stopAnimating];
        }
    }

Which also safely handles the case where the cell is nil.

Note: while this "works" it's still a naïve approach. A more sophisticated approach would cache the (decoded) images and possibly has some "look ahead and eager eviction strategy" for better user experience and lower memory foot-print.

Note that imageWithData: may still be costly since it may require to decode, decompress and resize the image before it can be rendered. Basically, this can be performed beforehand, with an offscreen context.

Upvotes: 0

Gaurav Singh
Gaurav Singh

Reputation: 1997

You can use SDWebcache library. It contains a UIImageView category class which can load images from urls. I find it works well with tableviews

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726809

The problem happens because the asynchronous image request finishes after your cell scrolls off the screen and gets reused. Downloads complete "out of order", contributing to a visual confusion. Essentially, some of the cells put up for reuse by scrolling, are still "hot", in the sense that their image load is in progress. Reusing such cell creates a race between the old and the new image downloads.

You should change the strategy that you use to load the images: rather than sending a request and "forgetting" it, consider using connectionWithRequest:delegate: method, storing the connection in the cell, and calling cancel on it when prepareForReuse method is called. This way your reused cells would be "cold".

Upvotes: 1

Related Questions