How to get UITableViewCell images to update to downloaded images without having to scroll UITableView

自作多情 提交于 2020-01-06 02:30:11

问题


I'm trying to pull my own flavor of the usual UITableView + async download + cache technique. What I'm doing is, for each cell that gets dequeued in cellForRowAtIndexPath:

1-Check if it's corresponding thumbnail image is already 'cached' in /Library/Caches
2-If it is, just use that.
3-If not, load a default image and enqueue an NSInvocationOperation to take care of it:
   4a-The NSInvocationOperation gets the image from a remote server
   4b-Does the UIGraphicsBeginContext thing to scale down the image to 40x40  
   4c-saves the scaled down version to /Library/Cache
   4d-'SHOULD' update the cell's image to the new downloaded and downsized image, if the cell is still visible. 

However, I can't figure out how to get the cells to update their images unless I manually scroll them off and back on screen. The only hack I've been able to pull is having the NSOperation call the main thread when done, through performSelectorOnMainThread and the main thread can then call [viewtable reloadData]. But this seems wasteful: I'm reloading the whole table each time a cell's new image is ready.

As a less wasteful approach, I have the main thread instead set a bool flag and then, when scrollViewDidEndDecelerating, if the flag was set, a call to [viewtable reloadData] is made. With this approach the cells only refresh when the user is done scrolling.

But still, I'd like for just the visible cells to update, if their cached images are ready while they are still visible (meaning the user didn't scroll them off the view).

Here's my code so far:

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

  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if (cell == nil) 
  {
    cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle
                                   reuseIdentifier: CellIdentifier] autorelease];

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    cell.selectionStyle = UITableViewCellSelectionStyleGray;
  }

  // Configure the cell...
  cell.textLabel.text = [[dbData objectAtIndex:indexPath.row] objectAtIndex:0];
  cell.detailTextLabel.text = [[dbData objectAtIndex:indexPath.row] objectAtIndex:1];

  NSString *ImageName = [[dbData objectAtIndex:indexPath.row] objectAtIndex:2];
  NSString *cachedImageName = [[[ImageName stringByDeletingPathExtension] stringByAppendingString:thumbnailSizeSuffix] stringByAppendingPathExtension:@"png"];
  NSString *cachedImagePath = [cachePath stringByAppendingPathComponent:cachedImageName];

  if([[NSFileManager defaultManager] fileExistsAtPath:cachedImagePath])
    cell.imageView.image = [UIImage imageWithContentsOfFile:cachedImagePath];
  else
  {
    cell.imageView.image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:defaultNeedsDownloadIconFile ofType:@"png"]];
    NSArray *package = [NSArray arrayWithObjects:ImageName, cachedImagePath ,referencingTable, nil];                                    
    NSInvocationOperation *concurrentImageLoader = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadURI:) object:package];
    [concurrentQueue addOperation: concurrentImageLoader];
    [concurrentImageLoader release];
  }

  return cell;
}

For the "kernel" of the NSInvocationOperation, I've tried this:

- (void)loadURI:(id)package
{
  NSArray *payload = (NSArray*)package;

  NSString *imageName = [payload objectAtIndex:0];
  NSString *cachedImagePath = [payload objectAtIndex:1];
  NSString *imageURL = [NSString stringWithFormat:@"http://www.useanddisposeof.com/VentanaSurDB/%@/photo/%@",[payload objectAtIndex:2], imageName]; 

  UIImage *newThumbnail = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]]];

  if(!newThumbnail)
    newThumbnail = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:defaultNotFoundIconFile ofType:@"png"]];

  UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, thumbnailSize.width, thumbnailSize.height)];
  imageView.layer.borderColor = [UIColor blackColor].CGColor;
  imageView.layer.cornerRadius = 4.0;
  imageView.layer.masksToBounds = YES;
  imageView.layer.borderWidth = 1.0;
  imageView.image = newThumbnail;

  UIGraphicsBeginImageContext(CGSizeMake(thumbnailSize.width, thumbnailSize.height));
    [imageView.layer renderInContext:UIGraphicsGetCurrentContext()];
    newThumbnail = UIGraphicsGetImageFromCurrentImageContext();      
  UIGraphicsEndImageContext();

  [imageView release];
  [UIImagePNGRepresentation(newThumbnail) writeToFile:cachedImagePath atomically:YES];
  [self performSelectorOnMainThread:@selector(updateCellImage) withObject:nil waitUntilDone:NO];
}

And this is the code back in the main thread for refreshing the tableview:

- (void)updateCellImage:(id)package
{
  needReloadCachedImages = YES;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
  // I know, I know, there's a race condition here.. I'll fix it if this code stays.
  if(needReloadCachedImages)
    [self.tableView reloadData];

  needReloadCachedImages = NO;
}

Any ideas?


回答1:


But this seems wasteful: I'm reloading the whole table each time a cell's new image is ready.

reloadData only reloads visible cells, not the whole table, which is what you say you want.




回答2:


How about giving some open-source a try? This is too much effort for a simpler problem. There is also a nice tutorial on this, which might give you an idea on what you might be doing wrong.



来源:https://stackoverflow.com/questions/7874505/how-to-get-uitableviewcell-images-to-update-to-downloaded-images-without-having

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!