【问题标题】:UITableView scrollToRowAtIndexPath scrolls to wrong offset with estimatedRowHeight on iOS 7UITableView scrollToRowAtIndexPath 滚动到 iOS 7 上的估计行高度错误的偏移量
【发布时间】:2014-01-20 11:22:59
【问题描述】:

我在 iOS 7 中使用 UITableView 中的 estimatedRowHeight 方法,该方法非常适合快速加载具有 5000 行可变高度的 UITableView。

我的 UITableView 由 50 个部分组成。每个部分有 100 行,高度可变。 一开始我使用estimatedRowHeight 进行快速加载,但之后当我调用scrollToRowAtIndexPath 时,我的 UITableView 滚动到错误的偏移量。我可以理解为什么会这样,因为它有一个estimatedRowHeight,直到我滚动整个表格并且在heightForRowAtIndexPath 委托方法中设置了正确的单元格高度。

有什么办法吗?

【问题讨论】:

  • 遗憾的是,Apple 在 iOS 9 中 2 年后仍未修复它。请欺骗这个雷达来敦促他们:openradar.me/20829131

标签: iphone uitableview ios7


【解决方案1】:

不幸的是,在您的代码中使用estimatedRowHeight值如此之低 时会出现此问题。当您 scrollToRowAtIndexPath 时,它不会主动计算正确的尺寸。根本原因是,如果您从第 1 节滚动到第 2 节,如果它是一个相对较新的设备,它可以实时计算正确的位置和单元格的estimatedRowHeight。例如,如果您必须处理 5000 个单元,任何旧设备都会被淘汰,甚至任何新设备也会如此。

您的问题的一个可能解决方案是增加estimatedRowHeight 常量,这样设备就不必做太多工作。

【讨论】:

  • 我不同意这是“预期”的行为——这是一个绝对可以而且应该由 Apple 修复的错误。在 iOS 7 针对这个问题发布后不久,我提交了一份 Radar(它也以其他方式显示,click here 以进一步讨论该错误和一些解决方法)。请向 Apple 提交更多错误报告,以便他们优先解决此问题。
  • 尽管@simleyborg 想一想,它只会跳到那个单元格,它当然不应该在运行之前计算所有东西的估计高度。我假设如果 iOS 7 仍然无法做到这一点,那么无论你提交多少 Radar,它都不会这样做。
  • 是什么让您认为它必须知道从当前滚动位置到您要滚动到的实际行高?它可以应用与第一次在 any 任意点以滚动位置加载表格视图时使用的完全相同的优化——表格视图显示可见单元格,并假设上面和下面的单元格是估计的高度,并基于此适当地定位/调整滚动指示器。然后当单元格在屏幕上滚动并修改估计值时,滚动指示器的位置/大小也会更新。
  • 不幸的是,这是不正确的。如果是这样的话,estimatedRowHeight 将没有任何好处!为了轻松证明这没有发生,请使用行高估计加载表视图。然后告诉表格视图滚动到表格视图底部的单元格。你会看到heightForRowAtIndexPath 调用中间跳过的单元格,如果你检查表格视图的contentOffset,你会发现它实际上等于estimatedRowHeight * numberOfCellsSkipped。很高兴上传示例项目,以便您自己轻松验证。
  • 让我投入两分钱。当我忘记更新估计的行常量而我的单元格布局已更改时,我也遇到了这个问题。我发现设置更高的值比设置更低的值更好,例如可能的最大单元格高度。这样,滚动到顶部就会按预期发生。
【解决方案2】:

这不是最漂亮的解决方案,但我使用了解决此问题的解决方法。始终将estimatedRowHeight 更新为最高的计算单元格,然后使用如下内容:

  1. 将 tableView 的内容滚动到零偏移量。
  2. 通过调用 scrollToRowAtIndexPath 来索引路径零来完成。

    [self.tableView setContentOffset:CGPointZero animated:YES];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
                              atScrollPosition:UITableViewScrollPositionTop
                                      animated:YES];
    });
    

结果 tableView 应该很流畅地滚动到顶部。

【讨论】:

    【解决方案3】:

    仍然没有可靠的解决方案,但对我来说,执行不带动画的滚动是可以接受的,尽管http://www.openradar.me/20829131 中提到了,但它滚动到具有正确偏移量的预期单元格时效果很好。

    self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition:.Top, animated: false)

    【讨论】:

      【解决方案4】:

      它似乎在系统布局视图后工作。我在 VC 的 viewDidAppear 中调用scrollToRowAtIndexPath,结果正确。这当然不是很漂亮,因为当视图已经可见时它会滚动......

      【讨论】:

        【解决方案5】:

        当调用scrollToRowAtIndexPath 时,不计算所有单元格的高度 b/w 当前位置和目标偏移量。只有其中一些是。

        相反,UITableView 使用estimatedRowHeight 来计算导致错误偏移的目标偏移。

        我遇到了同样的问题,我发现了一个小技巧(我不太喜欢)在初始 relaodData 之后只计算一次精确的单元格高度。我在下面插入了两行:

        tableView.reloadData()
        
        // only for the initial reloadData
        
        let numSections = tableView.numberOfSections
        let numRowsInLastSection = tableView.numberOfRowsInSection(numSections-1)
        
        // scrolls to the last row in the tableView
        tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numRows-1, inSection: numSections-1), atScrollPosition: .Bottom, animated: false)
        
        // back again to the original position(which is 0 for this is only called right after the initial reloadData)
        tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: false)
        

        这会导致 tableView 在屏幕上出现较晚,但我认为这是可以接受的,而不是在 tableView 出现后冻结 UI。在开始时调用 API 会更好,因为这感觉像是网络上的一点延迟,而不是 UI 延迟。

        在此之后,tableView 会跳转到单元格的确切位置。

        是的,我也不喜欢这个解决方案:|

        编辑:

        我这样做的唯一原因是通过调用cellForRowAtIndexPath: 来计算实际行高,但我发现当通过委托方法estimatedHeightForRowAtIndexPath: 为每一行提供适当的估计高度而不是给UITableView.estimatedRowHeight 提供静态值时解决了这个问题.

        我最终所做的是将willDisplayCell:forRowAtIndexPath: 中的行高缓存到磁盘并在estimatedHeightForRowAtIndexPath: 上使用该值。

        通过这样做,所有行的 estimatedHeightForRowAtIndexPath: 都会在开始时被调用,scrollToRowAtIndexPath 运行良好。

        【讨论】:

          【解决方案6】:

          我的解决方案是设置estimatedRowHeight to 0

          【讨论】:

            【解决方案7】:

            尝试在返回单元格之前在 -cellForRowAtIndexPath: 中添加此代码

            [cell layoutIfNeeded]
            

            【讨论】:

              【解决方案8】:

              这解决了我的问题

              let oldContentSize = tableView.contentSize
              tableView.scrollToRow(at: indexPath, at: .top, animated: true)
              // Called again when tableView contentSize change
              if !tableView.contentSize.equalTo(oldContentSize) {
                  tableView.scrollToRow(at: indexPath, at: .top, animated: true)
              }
              

              【讨论】:

                【解决方案9】:

                我的错误,因为“atScrollPosition”设置为UITableViewScrollPositionNone - 将其更改为UITableViewScrollPositionMiddle,它应该可以正常工作

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-08-25
                  • 2013-09-27
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-06-25
                  • 2015-02-01
                  相关资源
                  最近更新 更多