如何在调用-reloadData后保留UITableView contentoffset

avi*_*oss 57 uitableview reloaddata ios contentoffset

CGPoint offset = [_table contentOffset];
[_table reloadData];
[_table setContentOffset:offset animated:NO];    //unuseful

//    __block UITableView *tableBlock = _table;
//    [self performBlock:^(id sender) {
//        [tableBlock setContentOffset:offset];
//    } afterDelay:2];
Run Code Online (Sandbox Code Playgroud)

我知道不知道任何被调用的委托方法reloadData.使用afterDelay:2哪种黑客可能太短或太长,所以我该如何实现呢?

Mat*_*ala 108

我遇到了麻烦,因为我在cellForRowAtIndexPath方法中混淆了细胞大小.我注意到在执行reloadData之后调整大小的信息已经关闭,所以我意识到我需要在设置内容偏移之前立即强制它进行布局.

CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
Run Code Online (Sandbox Code Playgroud)

  • 我不明白接受的答案.我看到reloadData肯定会改变我的内容偏移量,这有助于它. (23认同)
  • 对于那些在`tableView:heightForRowAtIndexPath:`中使用`UITableViewAutomaticDimension`的人来说,为了让它在iOS 10下工作,我还必须在`tableView:estimatedHeightForRowAtIndexPath:`中返回``UITableViewAutomaticDimension`来使它工作.在这个方法中返回一个常量对我来说不起作用. (6认同)
  • `[tableView layoutIfNeeded];`解决了我的问题.谢谢! (3认同)

Sky*_*ker 87

调用reloadDatatableView不会更改内容偏移量.但是,如果您使用的UITableViewAutomaticDimension是iOS 8中引入的,则可能会出现问题.

当使用UITableViewAutomaticDimension,需要写委托方法tableView: estimatedHeightForRowAtIndexPath:并返回UITableViewAutomaticDimension随着tableView: heightForRowAtIndexPath:这也返回相同.

对我来说,在使用它时我在iOS 8中遇到了问题.这是因为estimatedHeightForRowAtIndexPath:即使我正在使用,方法方法也返回了不准确的值UITableViewAutomaticDimension.这是iOS 8的问题,因为iOS 9设备没有问题.

我通过使用字典存储单元格高度的值并返回它来解决了这个问题.这就是我做的.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = @(cell.frame.size.height);

    [self.cellHeightsDictionary setObject:height forKey:key];
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSNumber *key = @(indexPath.row);
    NSNumber *height = [self.cellHeightsDictionary objectForKey:key];

    if (height)
    {
        return height.doubleValue;
    }

    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewAutomaticDimension;
}
Run Code Online (Sandbox Code Playgroud)

检查高度是否存在是第一次加载页面.

  • 但我有一个改进!保存indexPath本身而不仅仅是行.由于我有多个部分,这是一个问题...... (5认同)
  • 这个答案有效.但是,如果单元格包含可以动态更改高度的项目(如UITextView),则需要在单元格高度更改时更改行的存储高度.我调用textViewDidChange()委托方法时更新高度.奇迹般有效. (4认同)
  • 对于使用自动布局的人来说,这是正确的答案.在'viewDidLoad'中,您需要为'self.tableView.estimatedRowHeight'设置一个非零值,然后调用'self.tableView.rowHeight = UITableViewAutomaticDimension'.然后,您需要覆盖'tableView(:heightForRowAt :)'以返回'UITableViewAutomaticDimension',并覆盖tableView(:estimatedHeightForRowAt :)来执行相同操作.什么是API,对吧?恒星. (2认同)

Nik*_*Kov 31

Swift 4变种@Skywalker回答:

fileprivate var heightDictionary: [Int : CGFloat] = [:]

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    heightDictionary[indexPath.row] = cell.frame.size.height
}

override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    let height = heightDictionary[indexPath.row]
    return height ?? UITableViewAutomaticDimension
}
Run Code Online (Sandbox Code Playgroud)

另一个解决方案(从MessageKit获取):

应该调用此方法而不是reloadData.这适合特定情况.

public func reloadDataAndKeepOffset() {
    // stop scrolling
    setContentOffset(contentOffset, animated: false)

    // calculate the offset and reloadData
    let beforeContentSize = contentSize
    reloadData()
    layoutIfNeeded()
    let afterContentSize = contentSize

    // reset the contentOffset after data is updated
    let newOffset = CGPoint(
        x: contentOffset.x + (afterContentSize.width - beforeContentSize.width),
        y: contentOffset.y + (afterContentSize.height - beforeContentSize.height))
    setContentOffset(newOffset, animated: false)
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢,设置估计的高度对我有用。我只实现了heightForRowAt索引路径。 (2认同)

Cyr*_*ril 25

默认情况下,reloadData保留contentOffset.但是,如果您确实具有不准确的estimatedRowHeight值,则可以更新它.

  • @Cyril 你说“这是一个估计值,不需要是你单元格的确切实际大小”,但也说“如果你的估计行高度值不准确”。那么我们究竟如何知道哪个是准确的呢?例如,我的单元格通常在 40 和 60 处,我设置为 50。但是,有时会弹出异常数据导致单元格的高度增长到 120,可能是 240 或 300。那么准确的估计行高度应该是多少? (2认同)

sar*_*pol 20

在我的情况下,取消选中行高自动并估计自动问题已解决

修复重装问题


Lou*_*uis 17

我最近在使用reloadData- reloadData不更改contentOffset或滚动表视图.如果偏移量小于新的数据量,它实际上保持不变.

  • 为什么这是公认的答案.我在这里看不到解决方案. (12认同)
  • UITableViewAutomaticDimension可能会导致偏移问题.这个答案也不是问题的答案. (7认同)
  • 我不同意这个答案。在我的通话记录中,我看到`scrollViewDidScroll`的调用是由`tableView.reloadData()`引起的:偏移实际上已更改。 (5认同)
  • `reloadData`不会改变contentOffset,但偏移量相对于可见单元格而改变. (3认同)

小智 11

如果实施estimatedHeightForRowAtIndexPath方法而您的估计不正确,则可能会遇到这种情况。

为了解决这个问题,您可以返回一个较大的高度,该高度大于tableView中每个单元格的高度,如下所示:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return 800.f; // if 800 is bigger than every possible value of your cell height.
}
Run Code Online (Sandbox Code Playgroud)


Boh*_*ych 7

如果在dataSource数组的开头插入数据,则需要更改contentOffset如下: Swift 3+

func prepareReloadData() {
    let previousContentHeight = tableView.contentSize.height
    let previousContentOffset = tableView.contentOffset.y
    tableView.reloadData()
    let currentContentOffset = tableView.contentSize.height - previousContentHeight + previousContentOffset
    tableView.contentOffset = CGPoint(x: 0, y: currentContentOffset)
}
Run Code Online (Sandbox Code Playgroud)