views:

690

answers:

1

I am having some issues with updating my UITableViewCells after asynchronously downloading the cell image. I am using custom UITableViewCells like so:

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

    if (cell == nil) {
        cell = [[[MainCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"productCell"] autorelease];
    }
}

I have a class that is the UITableViewCell and then a class that does all the drawing. It's the sample code from Apple called "AdvancedTableViewCells" and I am doing composite.

I have images in my cells, which I download asynchronously using Apple's sample code called "LazyTableImages". Here's the delegate that should be updating the cell:

- (void)coverImageDidLoad:(NSIndexPath *)indexPath {
    CoverImageAsyncLoader *coverImageAsyncLoader = [imageDownloadsInProgress objectForKey:indexPath];
    if (coverImageAsyncLoader != nil) {
        MainTableViewCell *cell = (MainTableViewCell *)[self.tableView cellForRowAtIndexPath:coverImageAsyncLoader.indexPathInTableView];

        // Display the newly loaded image
  if (coverImageAsyncLoader.products.coverImage != nil) {
   cell.productCover = coverImageAsyncLoader.products.coverImage;
  } else {
   cell.productCover = blankCoverImage;
  }
    }
}

But nothing happens. A friend tells me I cannot update the UI from a background thread, but since I am doing it through a delegate I am not sure why it isn't updating. I have tried:

 [cell setNeedsDisplay];
 [cell.contentView setNeedsDisplay];

and also to set cell as:

 cell = [[[MainCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"productCell"] autorelease];

and then update the cell's display.

But nothing works :( I can post more code, but the post is already a bit long.

A: 

If you know the index path's row and section to your cell in question (e.g. myCellIndexPath.section and myCellIndexPath.row), take a look at reloading a UITableView cell with the table view's -reloadRowsAtIndexPaths:withRowAnimation: method.

For example:

[tableView beginUpdates];
NSUInteger _path[2] = {myCellIndexPath.section, myCellIndexPath.row};
NSIndexPath *_indexPath = [[NSIndexPath alloc] initWithIndexes:_path length:2];
NSArray *_indexPaths = [[NSArray alloc] initWithObjects:_indexPath, nil];
[_indexPath release];
[tableView reloadRowsAtIndexPaths:_indexPaths withRowAnimation:UITableViewRowAnimationRight];
[_indexPaths release];
[tableView endUpdates];

As this updates the UI, you will want this in a method that is run on the main thread.

There are other row animations, depending on taste (UITableViewRowAnimationFade, UITableViewRowAnimationNone, etc.). Search your help on the UITableViewRowAnimation struct.

Alex Reynolds
Thanks... It works, but not perfectly. It does update the cells, but if I do any scrolling or use the status bar to scroll to the top, it crashes the app. There's also some random glitches where it insert a new 'twin' row above one of the cells. And certain cells just won't update.
Canada Dev
You may not be creating and dequeing the cell correctly. Essentially, instead of "updating" a cell, you need to think about "calculating" a cell in the "cellForRow" method, based on the current state of data for all your cells.
Alex Reynolds
In other words, the `-reloadRowsAtIndexPaths:withRowAnimation:` ends up eventually calling `-tableView:cellForRowAtIndexPath:`. Make sure your cells are dequeued properly (e.g. "uniquely") in this method, that you are updating the cell contents in this method or in a method called from this method.
Alex Reynolds