重新加载行以更新 UITableView 中的单元格高度时出现问题

Posted

技术标签:

【中文标题】重新加载行以更新 UITableView 中的单元格高度时出现问题【英文标题】:Problems when reloading rows to update cell height in UITableView 【发布时间】:2014-09-24 11:12:32 【问题描述】:

我有一个自定义的UITableViewCell,我使用AFNetworking 将来自URL 的UIImageView 中的图像设置为。加载图像后,我想调整图像视图和单元格的大小以适合图像。

在我的tableView:cellForRowAtIndexPath: 我有以下代码:

FeedItem *feedItem = self.dataArray[indexPath.row];
FeedItemCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"FeedItemCell" forIndexPath:indexPath];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:feedItem.thumbnailUrl];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[cell.thumbnailImageView setImageWithURLRequest:request placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) 
    cell.thumbnailImageView.image = image;
    // first I set the height of the image view
    cell.thumbnailImageHeightConstraint.constant = image.size.height;
    // then I save the height in a dictionary to return it in tableView:heightForRowAtIndexPath:
    [thumbnailHeights setObject:[NSNumber numberWithFloat:thumbnailHeight] forKey:[NSNumber numberWithInteger:indexPath.row]];

    // now I reload the cell in order to have the new height applied
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) 
    NSLog(@"failed loading: %@", error);
];

在我的tableView:heightForRowAtIndexPath: 我有这个:

NSNumber *thumbnailHeightNumber = [thumbnailHeights objectForKey:[NSNumber numberWithInteger:indexPath.row]];
if (thumbnailHeightNumber != nil)  // image size has been calculated
    return thumbnailHeightNumber.floatValue;

return 0;

这类作品。在图像视图和单元格上都正确设置了高度。但是,有一些不可预知的行为:

    当我滚动时,某些单元格有时会闪烁,好像正在重新加载,这很烦人。 有时会在行中显示错误的单元格。实际上,tableView:cellForRowAtIndexPath: 返回的单元格的内容是正确的,但它显示了来自之前一个单元格的内容。 有时单元格是完全空白的。同样,检查其内容似乎是正确的。

我注意到了一些可能与该问题相关的内容。我的 tableView:cellForRowAtIndexPath: 方法总是被调用两次,我相信这实际上是导致我的问题的原因。如果我删除[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 行,它只被调用一次(对于每一行)并且问题停止,但是当然单元格的高度不会更新。

另外,我不认为这个问题与单元格高度的实际变化有关。如果我为图像和单元格高度设置了固定高度,调用reloadRowsAtIndexPaths:withRowAnimation: 时仍然会得到相同的副作用。

因此,我希望有解决此问题的方法,或通过其他方式来实现我的动态单元格高度目标。

【问题讨论】:

【参考方案1】:

setImageWithURLRequest:request:placeholderImage:success:failure: 的调用是异步的,因此cell.thumbnailImageView 将不会被填充,或者(如果正在重新使用单元格)在setImageWithURLRequest 成功之前将有以前的图像数据,可能在@987654324 之后的一段时间@ 已经返回了单元格。如果setImageWithURLRequest 失败,那么您的单元格将保留未更改/未配置的缩略图视图。

您可以在placeholder: 参数中提供一个图像,该图像将在调用success 块之前一直显示,那么至少您还有一个失败案例的后备方案。

一段时间后,随着各个单元格的成功块被异步调用,表格可能会闪烁,每个单元格都会强制重新加载不同的行。

除非您事先知道缩略图都是固定大小的(然后可以轻松使用标准占位符图像),否则我建议您在表格视图的 numberOfRowsInSection: 更改其返回值之前预加载相关的缩略图从而触发对tableView:cellForRowAtIndexPath: 的调用。这样您就可以在单元格中返回完全配置的图像。

【讨论】:

我理解你关于异步执行的观点,但这是我在互联网上发现人们的做法,包括AFNetworking 提供的示例。在我写的时候,如果我忽略对reloadRowsAtIndexPaths:withRowAnimation: 的调用,问题就不会发生,并且图像加载得很好(在正确的行中)。不幸的是,我不知道图像的大小,因此不知道整个练习。我不确定按照您的建议预加载图像是一个好主意,因为我不想加载所有图像。 tableView:cellForRowAtIndexPath: 仅在单元格即将变得可见时调用。 是的,关于不想不必要地预加载所有图像的好处。是否可能没有在主线程上调用 reloadRowsAtIndexPaths:withRowAnimation: ? 好吧,我想这是可能的,因为它是从完成块内部调用的。

以上是关于重新加载行以更新 UITableView 中的单元格高度时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

在 UITableView (swift2) 中选择单元格时更新/重新加载 ViewController

UITableView 在同一批次更新中重新加载和移动行导致“尝试为单元格创建两个动画”

UITableView, reloadData 重新加载单元格但不调整表格大小

在 iOS 中重新加载特定的 UITableView 单元格

重新加载 UITableView 而不重新加载标题部分

更改单元格中的数据后,UITableView 中的自定义单元格将无法正确重新加载