我有自定义单元格的表格视图。每个单元格都有图像,标题和描述。当我第一次加载表时,它加载正常。如果我慢慢滚动图像,也似乎工作正常。一旦我快速向下滚动(假设单元格的数量足够大而不适合而不向上和向下滚动),图像就会以随机顺序开始更改单元格。一些细胞具有相同的图像两次。
有什么理由发生这种情况?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
BookmarkCellViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NewsArticle *currArt = [self.lN_Dept objectAtIndex:indexPath.row];
if(currArt.artImage == Nil)
{
if([currArt.mainImage_URL rangeOfString:@"<img src="].location != NSNotFound)
{
NSRange range = [currArt.mainImage_URL rangeOfString:@"<img src=\"/CONC/"];
NSString *substring = [[currArt.mainImage_URL substringFromIndex:NSMaxRange(range)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSRange range2 = [substring rangeOfString:@"\""];
NSString *substring2 = [[substring substringToIndex:NSMaxRange(range2)-1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *imageURL = [PUB_URL stringByAppendingString:substring2];
[self downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
currArt.artImage = image;
cell.ArtDisplayImage.image = currArt.artImage;
}
}];
}
}
else
{
cell.ArtDisplayImage.image = currArt.artImage;
}
[cell configureCellForEntry:currArticle];
return cell;
}
- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *Data, NSError *error) {
if ( !error )
{
UIImage *image = [[UIImage alloc] initWithData:Data];
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
答案 0 :(得分:2)
之所以发生这种情况,是因为您误解了UITableView
的工作原理。让我们假设您的表视图有100个单元格,它可以同时显示10个单元格。加载表视图时,它会创建10个单元格实例。当您开始向下滚动表格视图时,它实际上不会创建单元格的新实例 - 重新使用从屏幕上消失的单元格(因为您正在调用[tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
)。这意味着索引10处的单元格将具有与索引0处的单元格相同的参考
回到你的问题,你正在加载图像
[self downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
currArt.artImage = image;
cell.ArtDisplayImage.image = currArt.artImage;
}
}];
如果图像下载得更快,那么单元格会被重复使用(当你慢慢滚动时会发生这种情况)一切都会好起来的。但是如果在下载图像之前重新使用了单元格,那么内存中有两个块正在下载该单元格的图像。要解决此问题,我建议您使用SDWebImage自动处理这种情况,或取消下载从屏幕上消失的单元格的图像。希望这会有所帮助。
答案 1 :(得分:1)
这里可能存在竞争条件。下载排队等待给定的单元格,然后重新使用该单元格并设置另一个图像或下载排队,但是第一次下载完成并设置图像,即使单元格现在代表不同的元素。如果您想查看这确实是问题,请禁用单元重用(为每一行实例化一个新单元格)。如果问题消失了,那就是它。
您可以做的一件事是取消由给定单元格启动的现有下载(如果该单元格被重用)。查看以这种方式处理此问题的开源库SDWebImage。
您可以做的另一件事是让单元格存储当前正在尝试加载的URL,并将结果下载的URL与单元格存储的URL进行比较,以查看它们是否仍然相同(并且仅设置图像,如果它们仍然相同)。
这样的事情应该这样做:
if(currArt.artImage == Nil)
{
if([currArt.mainImage_URL rangeOfString:@"<img src="].location != NSNotFound)
{
NSRange range = [currArt.mainImage_URL rangeOfString:@"<img src=\"/CONC/"];
NSString *substring = [[currArt.mainImage_URL substringFromIndex:NSMaxRange(range)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSRange range2 = [substring rangeOfString:@"\""];
NSString *substring2 = [[substring substringToIndex:NSMaxRange(range2)-1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *imageURL = [PUB_URL stringByAppendingString:substring2];
cell.imageURL = imageURL; // you need to add an imageURL string property to your cells
// you need to return the URL string in your block to compare it
[self downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image, NSString *url)
{
if (succeeded)
{
currArt.artImage = image;
if(cell.imageURL isEqualToString:url])
{
cell.ArtDisplayImage.image = currArt.artImage;
}
}
}];
}
}
else
{
cell.ArtDisplayImage.image = currArt.artImage;
cell.imageURL = nil; // make sure it is not overwritten
}
答案 2 :(得分:1)
这样的事情:
[self downloadImageWithURL:[NSURL URLWithString:imageURL] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded) {
YourCellType *cellToUpdate = [tableView cellForIndexPath:indexPath];
if (cellToUpdate)
{
currArt.artImage = image;
cellToUpdate.ArtDisplayImage.image = currArt.artImage;
}
}
}];