我使用异步块(Grand central dispatch)来加载我的单元格图像。但是,如果你快速滚动它们仍然会出现但速度非常快,直到它加载了正确的一个。我确定这是一个常见问题,但我似乎无法找到它。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Load the image with an GCD block executed in another thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *offersImage = [UIImage imageWithData:data];
cell.imageView.image = offersImage;
});
});
cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title];
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle];
return cell;
}
答案 0 :(得分:18)
至少,您可能希望在dispatch_async
之前删除单元格中的图像(如果它是重复使用的单元格):
cell.imageView.image = [UIImage imageNamed:@"placeholder.png"];
或者
cell.imageView.image = nil;
您还希望在更新之前确保相关的单元格仍在屏幕上(使用UITableView
方法,cellForRowAtIndexPath:
如果该行的单元格为{1}},则返回nil
不再可见,不要与UITableViewDataDelegate
方法tableView:cellForRowAtIndexPath:
混淆,例如:
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.imageView.image = [UIImage imageNamed:@"placeholder.png"];
// Load the image with an GCD block executed in another thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
if (data) {
UIImage *offersImage = [UIImage imageWithData:data];
if (offersImage) {
dispatch_async(dispatch_get_main_queue(), ^{
UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
if (updateCell) {
updateCell.imageView.image = offersImage;
}
});
}
}
});
cell.textLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] title];
cell.detailTextLabel.text = [[[appDelegate offersFeeds] objectAtIndex:indexPath.row] subtitle];
return cell;
坦率地说,您还应该使用缓存来避免不必要地重新检索图像(例如,向下滚动一点并向上滚动,您不希望为这些先前单元格的图像发出网络请求)。更好的是,您应该使用其中一个UIImageView
类别(例如SDWebImage或AFNetworking中包含的类别)。这样可以实现异步图像加载,但也可以优雅地处理缓存(不要重新检索几秒前刚刚检索到的图像),取消尚未发生的图像(例如,如果用户快速滚动到第100行,您可能不会在向用户显示第100行的图像之前,我不想等待前99检索。
答案 1 :(得分:2)
这是异步图像加载的问题...
假设您在任何给定时间都有5个可见行。
如果您正在快速滚动,并向下滚动10行,tableView:cellForRowAtIndexPath
将被调用10次。问题是这些调用比返回图像更快,并且您有来自不同URL的10个待处理图像。
当图像最终从服务器返回时,它们将在您放入异步加载器的那些单元格上返回。由于您只重复使用5个单元格,因此从服务器下载这些图像时,其中一些图像将在每个单元格上显示两次,这就是您看到闪烁的原因。另外,记得打电话
cell.imageView.image = nil
在调用异步下载方法之前,因为重新使用的单元格中的前一个图像将保留,并且在分配新图像时也会导致闪烁效果。
解决这个问题的方法是存储您必须在每个单元格上显示的图像的最新URL,然后当图像从服务器返回时,检查该URL与您请求中的URL。如果不相同,请暂存该图像以供日后使用。 对于缓存请求,请查看NSURLRequest和NSURLConnection类。
我强烈建议您使用AFNetworking进行任何服务器通信。
祝你好运!答案 2 :(得分:2)
你闪烁的原因是你在滚动过程中开始下载几张图片,每次你在屏幕上显示一个单元格时就会执行一个新的请求,并且可能没有完成旧请求,每个图块都有一个请求完成图像设置在单元格上,所以如果你快速滚动你使用让我们说一个单元格3次= 3次将被触发的请求=将在该单元格上设置3个图像=闪烁。
我有同样的问题,这是我的方法:
创建包含所有必需视图的自定义单元格。每个单元都有自己的下载操作。在单元格的-prepareForReuse
方法中。我会将图像设为nil并取消请求。
对于每个单元格,我只有一个请求操作=一个图像=没有闪烁。
即使使用AFNetworking
,如果您不取消图像下载,也会出现同样的问题。
答案 3 :(得分:0)
问题是您在主线程上下载了图像。为此调度后台队列:
dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL);
// execute a task on that queue asynchronously
dispatch_async(myQueue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[[appDelegate offersFeeds] objectAtIndex:indexPath.row] imageurl]]];
//UI updates should remain on the main thread
UIImage *offersImage = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *offersImage = [UIImage imageWithData:data];
cell.imageView.image = offersImage;
});
});