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

时间:2011-12-27 01:31:19

标签: ios uitableview reloaddata 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];

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

13 个答案:

答案 0 :(得分:99)

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

CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];

答案 1 :(得分:84)

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

使用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;
}

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

答案 2 :(得分:25)

@Skywalker的Swift 4变种回答:

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
}

另一个解决方案(从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)
}

答案 3 :(得分:21)

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

答案 4 :(得分:15)

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

fix reload problem

答案 5 :(得分:13)

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

答案 6 :(得分:6)

如果在dataSource数组的开头插入数据,则需要像这样更改Observables Swift 3 +

contentOffset

答案 7 :(得分:6)

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

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

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

答案 8 :(得分:2)

我遇到了同样的问题,但这里没有提出任何答案。这是我如何解决它。子类UITableView并覆盖layoutSubviews方法,如下所示:

override func layoutSubviews() {
    let offset = contentOffset
    super.layoutSubviews()
    contentOffset = offset
}

答案 9 :(得分:2)

@ Skywalker的回答显示了估计细胞高度问题的最佳解决方法。但有时在不同的地方会出现问题 有时问题会在表视图的contentInsets中出现。如果在屏幕上看不到tableView时重新加载数据,则在屏幕上出现表格视图后,您可能会面对错误的偏移。
这是因为UIViewController可以控制插件,如果他的scrollView在scrollView出现时允许将scrollView放在透明navigationBar和statusBar下面。 我在iOS 9.1中遇到过这种行为

答案 10 :(得分:1)

马特的上述回答使我意识到estimatedRowHeight一定是一个问题。

因此,正如一些人指出的那样,reloadData不应修改contentOffset,因此在设置rowHeight = UITableView.automaticDimension时,只是要确保设置正确的estimatedRowHeight

如果estimatedRowHeight比估计的UITableView短,它将尝试固定高度,并且滚动行为将出现,但前提是出现高度问题的单元格可见。换句话说,请确保您的estimatedRowHeight设置正确。

我希望这可以帮助其他人。

答案 11 :(得分:-3)

这是100%的工作

change the tableView.reloadData() 

tableView.reloadRows(at: tableView!.indexPathsForVisibleRows!, with: .none)

答案 12 :(得分:-4)

因为它工作正常

[tView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
             atScrollPosition:UITableViewScrollPositionTop
                     animated:NO];