重新加载单元格后向上滚动时,动态单元格高度跳跃的UITableView

时间:2015-09-07 14:20:20

标签: ios objective-c swift uitableview

我有一个表视图,每个单元格可能有自己的高度,因此不适合使用rowHeight。相反,我现在正在使用let indexSet = NSIndexSet(index: 10)self.tableView.estimatedRowHeight = 75。这意味着它调用单元格上的sizeThatFits函数,以确定其高度。这一切都运作良好。

当您重新加载屏幕上的单元格时,问题就出现了。例如,向下滚动以显示单元格10,然后重新加载单元格10,工作正常。但是当你开始向上滚动,经过你已经看过的单元格时,它会恢复到每个单元格的estimatedRowHeight,完全忽略sizeThatFits,因此在滚动时跳转。我不可能给出一个准确或“足够好”的估计值,因此这种跳跃不会引人注意,因为我的细胞能够显示一行文字或一个完整的图像 - 这是一个很大的区别。大小

我在这里看到了这个效果:

https://vid.me/edgW

我在这方面做了很多不同的尝试,使用了heightForRowAtIndexPath,estimatedHeightForRowAtIndexPath等的混合物。我在StackOverflow上尝试了各种建议。似乎没什么用。

我附上了一个非常简单的示例项目,您可以自己尝试:

https://www.dropbox.com/s/8f1rvkx9k23q6c1/tableviewtest.zip?dl=0

  1. 运行项目。
  2. 滚动,直到查看单元格10。
  3. 等待最多5秒钟让细胞重新加载(它变成紫色)。
  4. 向上滚动。
  5. 值得注意的是 - 如果单元格在重新加载时不在视图中,则会发生 not 。如果它位于当前滚动点下方的之上,则一切都按预期工作。

5 个答案:

答案 0 :(得分:17)

这种行为似乎是一个错误,如果没有其他原因,它在iOS 9上不再可复制。我确定没有太多的安慰。

这个问题主要源于估计不准确,如@NickCatib所说。你可以在iOS 8上做的最好的事情就是改进估算。许多人建议的技术是在willDisplayCell中缓存高度,并在后续调用estimatedRowHeightAtIndexPath时使用它们。

您可以通过不做任何让UITableView丢弃其缓存的操作来缓解行为,例如直接使用cellForRowAtIndexPath修改单元格中的内容,而不是使用重新加载{39}。 ;在屏幕上。但是,如果您确实需要更改单元格的高度,那将无济于事。

我不敢说错误无法在桌面视图中轻松修复,因为您无法控制布局。通过在失效期间更改UICollectionViewFlowLayout,可以更轻松地在子类contentOffsetAdjustment中解决该错误,尽管这可能不是非常容易。

答案 1 :(得分:11)

我也有这个问题,并使用了SO的解决方案,我现在无法找到。如果我这样做,我会添加链接。这是解决方案:

问题在于表格视图没有正确估计行高。要解决此问题,请先将高度缓存在willDisplayCell中,然后再使用该高度。

<强>代码

在viewDidLoad中:

heightAtIndexPath = [NSMutableDictionary new];


- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = @(cell.frame.size.height);
    [heightAtIndexPath setObject:height forKey:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if([heightAtIndexPath objectForKey:indexPath]) {
        return [[heightAtIndexPath objectForKey:indexPath] floatValue];
    } else {
        return UITableViewAutomaticDimension;
    }
}

答案 2 :(得分:2)

在iOS 13.5.1中:

我有一个tableView,其中包含4种类型的单元格,所有单元格具有不同的动态高度。 我在这里遵循了公认的解决方案,但是要解决跳跃效果,我需要在重新加载表格视图时添加更多内容:

从接受的答案中,我添加了以下代码: 声明此变量

var allCellHeights = [IndexPath : CGFloat]()

然后添加2种方法:

 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
     self.allCellHeights[indexPath] = cell.frame.size.height
 }
 func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
     return self.allCellHeights[indexPath] ?? UITableView.automaticDimension
 }

现在,我在重新加载tableview时必须添加的额外代码:

let contentOffset = self.tableView.contentOffset
self.tableView.reloadData()
self.tableView.layoutIfNeeded()
self.tableView.setContentOffset(contentOffset, animated: false)

还要检查此答案:reloadData() of UITableView with Dynamic cell heights causes jumpy scrolling

答案 3 :(得分:0)

呃......这是一个很难处理的问题。

我们来看看facebook吧。他们的时间表也存在同样的问题,他们最终在某种网络视图中做到了。

我在某种时间轴上遇到了类似的问题,使用了自动行高并遇到了这个问题。解决它的第一件事是将estimatedHeight设置为尽可能接近平均细胞高度。这很难处理,因为你可能有文字(高50)或图像+文字(高1500)。接下来要改进的是实现estimatedHeight forIndexPath,它基本上为不同的indexPath返回不同的估计高度。

之后还有很多其他的解决方案,但这个方案尽可能接近可变高度(差异很大)。

答案 4 :(得分:-2)

我遇到了同样的问题,我的表工作正常,直到tableview重新加载。所以我找到了一个解决方案,只使用rowHeight而不是估计的高度。如果你有不同的身高。所以请提供完整的代码,以便我提供解决方案。我有一个像Instagram页面的单元格。我通过计算高度的heightforrow方法工作正常。但在这种情况下,估计的身高不会很好。如果你使用下面的代码,它工作正常。请试试。

self.tableView.rowHeight = 75 //it will be your dynamic height
//self.tableView.estimatedRowHeight = 75

如果您混淆计算每个单元格的行高,只需发布​​样本我将提供解决方案。如果我能 谢谢