我有一个填充了搜索字词结果的tableview。许多结果都有需要从URL加载的图像,但不是全部。我最初在主线程中的cellForRowAtIndexPath
方法中抓取了URL中的图像,这种方法运行得很好,但是当桌面视图在每个图像上“卡住”时,它使得滚动看台不稳定。
所以,我决定尝试在后台线程中加载图像。这是我的cellForRowAtIndexPath
方法。 URL存储在resultsArray中,索引对应于单元格的行。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[sBar resignFirstResponder];
if (indexPath.row != ([resultsArray count]))
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ResultCell"];
UIImageView * bookImage = (UIImageView *)[cell viewWithTag:102];
//set blank immediately so repeats are not shown
bookImage.image = NULL;
//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, ^{
NSData *image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[[resultsArray objectAtIndex:indexPath.row] thumbnailURL]]];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
bookImage.image = [UIImage imageWithData:image];
if(bookImage.image == nil)
{
bookImage.image = [UIImage imageNamed:@"no_image.jpg"];
}
});
});
return cell;
}
// This is for the last cell, which loads 10 additional items when touched
// Not relevant for this question, I think, but I'll leave it here anyway
else{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"More"];
UILabel *detailLabel = (UILabel *)[cell viewWithTag:101];
detailLabel.text = [NSString stringWithFormat:@"Showing %d of %d results", [resultsArray count], [total intValue]];
if ([UIApplication sharedApplication].networkActivityIndicatorVisible == NO) {
cell.userInteractionEnabled = YES;
}
return cell;
}
}
当tableview缓慢滚动时,图像全部加载到适当的单元格中,我可以确认,因为图像与单元格的标题匹配,但我省略了除图像之外的所有内容的设置以缩短此问题。但是当我滚动得更快时,特别是当我模拟慢速互联网连接时,图像开始加载错误的单元格。我认为这与重复使用单元格有关,因为如果我快速向上滚动,那么离开视图的单元格图像通常会在刚进入的单元格中结束。我以为bookImage.image = NULL;线将确保没有发生,但我猜不是。我想我不太了解背景线程,当图像最终从URL加载时,它是否忘记了它的目的是哪个单元?你知道可能会发生什么吗?感谢您的任何反馈!
答案 0 :(得分:1)
猜测:
修复此问题需要保留异步请求的句柄,并在单元格出列时取消它。
答案 1 :(得分:1)
我的方法(依赖于AFNetworking库)。将以下zip文件中的类包含在项目中:
http://dl.dropbox.com/u/6487838/imageloading.zip
创建自己的自定义单元格。代码可能与以下内容类似(请查看-setComment:
和-willMoveToSuperview:
方法):
#import "CommentCell.h"
#import "CommentCellView.h"
#import "MBImageLoader.h"
@interface CommentCell ()
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) CommentCellView *cellView;
@property (nonatomic, assign) CellStyle style;
@end
@implementation CommentCell
@synthesize cellView, style = _style, imageView, comment = _comment, showCounter;
- (id)initWithStyle:(CellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
{
self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
if (self)
{
self.selectionStyle = UITableViewCellSelectionStyleNone;
self.opaque = YES;
self.style = style;
self.showCounter = YES;
CGFloat width = 53.0f;
CGFloat x = (style == CellStyleRight) ? 320.0f - 10.0f - width : 10.0f;
CGRect frame = CGRectMake(x, 10.0f, width, 53.0f);
self.imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:@"User.png"];
[self.contentView addSubview:imageView];
self.cellView = [[CommentCellView alloc] initWithFrame:self.frame cell:self];
cellView.backgroundColor = [UIColor clearColor];
[self.contentView addSubview:cellView];
}
return self;
}
- (void)dealloc
{
self.comment = nil;
self.cellView = nil;
self.imageView = nil;
}
- (void)willMoveToSuperview:(UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
if (!newSuperview)
{
[[MBImageLoader sharedLoader] cancelLoadImageAtURL:_comment.iconURL forTarget:self];
}
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)setFrame:(CGRect)newFrame
{
[super setFrame:newFrame];
CGRect bounds = self.bounds;
bounds.size.height -= 1;
cellView.frame = bounds;
}
- (void)setNeedsDisplay
{
[super setNeedsDisplay];
[cellView setNeedsDisplay];
}
- (void)setComment:(MBComment *)comment
{
if (_comment == comment)
return;
_comment = comment;
if (comment.icon == nil)
{
imageView.image = nil;
[[MBImageLoader sharedLoader] loadImageForTarget:self withURL:comment.iconURL success:^ (UIImage *image)
{
comment.icon = image;
imageView.alpha = 0.0f;
imageView.image = image;
[UIView animateWithDuration:0.5f delay:0.0f options:UIViewAnimationOptionAllowUserInteraction animations:^ {
imageView.alpha = 1.0f;
} completion:^ (BOOL finished) {}];
} failure:^ (NSError *error)
{
DLog(@"%@", error);
}];
}
else
{
imageView.image = comment.icon;
}
[self setNeedsDisplay];
}
@end
答案 2 :(得分:0)
我是怎么做到的。
cellForRowAtIndexPath:
方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"CellIdentificator";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
[cell.imageView setImage:nil];
NSData *const cachedImageData = [self.cache objectAtIndex:indexPath.row];
if ([cachedImageData isKindOfClass:[NSData class]]) {
[cell.imageView setImage:[UIImage imageWithData:cachedImageData]];
} else {
[self downloadAndCacheImageForIndexPath:indexPath];
}
return cell;
}
downloadAndCacheImageForIndexPath:
方法:
- (void)downloadAndCacheImageForIndexPath:(NSIndexPath *)indexPath {
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
NSString *const imageStringURL = [NSString stringWithFormat:@"%@%.2d.png", IMG_URL, indexPath.row];
NSData *image = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:imageStringURL]];
[self.cache replaceObjectAtIndex:indexPath.row withObject:image];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:0]]withRowAnimation:UITableViewRowAnimationNone];
});
[image release];
});
}
self.cache
是NSMutableArray
,用于存储NSData
加载的图片。 self.cache
预先加载了[NSNull null]
个图片数量
BR。
尤金。