我正在尝试通常使用UITableView +异步下载+缓存技术。我正在做的是,对于在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.
然而,我无法弄清楚如何让细胞更新它们的图像,除非我手动将它们滚动并返回屏幕。我能够完成的唯一一个hack就是让NSOperation在完成后通过performSelectorOnMainThread调用主线程,然后主线程可以调用[viewtable reloadData]。但这似乎很浪费:每次细胞的新图像准备就绪时,我正在重新加载整个表格。
作为一种不那么浪费的方法,我让主线程改为设置bool标志然后,当scrollViewDidEndDecelerating时,如果设置了标志,则调用[viewtable reloadData]。使用此方法,单元格仅在用户完成滚动时刷新。
但是,我仍然希望更新可见单元格,如果它们的缓存图像在它们仍然可见时就已准备好(意味着用户没有将它们从视图中滚动)。
到目前为止,这是我的代码:
- (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;
}
对于NSInvocationOperation的“内核”,我试过这个:
- (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];
}
这是用于刷新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;
}
有什么想法吗?
答案 0 :(得分:2)
但这似乎很浪费:我每次都要重新加载整张桌子 细胞的新图像准备就绪。
reloadData
只重新加载可见的单元格,而不是整个表格,这就是你想要的。
答案 1 :(得分:1)
如何给open-source try a nice tutorial一些?这对于更简单的问题来说太费力了。此处还有{{3}},可能会让您了解自己可能做错了什么。