62
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];

I know don't know of any delegate method which gets called after reloadData. And using afterDelay:2 which is kind of a hack may be too short or too long, so how can I implement it?

4

13 回答 13

121

我遇到了麻烦,因为我在 cellForRowAtIndexPath 方法中弄乱了单元格大小。我注意到在执行 reloadData 后尺寸信息已关闭,因此我意识到我需要在设置内容偏移量之前立即强制它进行布局。

CGPoint offset = tableView.contentOffset;
[tableView.messageTable reloadData];
[tableView layoutIfNeeded]; // Force layout so things are updated before resetting the contentOffset.
[tableView setContentOffset:offset];
于 2015-07-09T17:14:17.063 回答
96

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

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

于 2016-01-22T05:32:39.133 回答
46

@Skywalker 答案的 Swift 5 变体:

private var heightDictionary: [IndexPath: CGFloat] = [:]

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

public func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    let height = heightDictionary[indexPath]
    return height ?? UITableView.automaticDimension
}

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

应该调用此方法而不是reloadData. 这可以适用于特定情况。

extension UITableView {
    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)
    }
}
于 2017-10-10T22:27:14.667 回答
29

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

修复重载问题

于 2018-03-28T15:51:23.063 回答
28

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

于 2015-08-21T08:41:56.877 回答
12

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

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

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { 
    return 800.f; // if 800 is bigger than every possible value of your cell height.
}
于 2018-07-27T03:13:33.090 回答
10

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

于 2011-12-27T02:28:45.557 回答
9

如果在 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)
}
于 2017-11-30T11:28:48.823 回答
2

@Skywalker 的回答显示了估计细胞高度问题的最佳解决方法。但有时问题碱液在不同的地方。
有时问题出在表格视图的 contentInsets 中。如果您在 tableView 在屏幕上不可见时重新加载数据,您可能会在 table view 出现在屏幕上后面临错误的偏移量。
发生这种情况是因为 UIViewController 可以控制插入,如果当滚动视图出现时他的滚动视图允许滚动视图位于透明导航栏和状态栏下方。
我在 iOS 9.1 中遇到过这种行为

于 2016-12-09T04:12:56.993 回答
2

我遇到了同样的问题,但是这里建议的答案都没有奏效。这就是我解决它的方法。子类UITableView和覆盖layoutSubviews方法是这样的:

override func layoutSubviews() {
    let offset = contentOffset
    super.layoutSubviews()
    contentOffset = offset
}
于 2016-08-24T02:15:39.453 回答
1

马特上面的回答让我意识到这一定是estimatedRowHeight.

所以正如几位指出的不reloadData应该修改,contentOffset所以当你设置你的rowHeight = UITableView.automaticDimension只是为了确保设置一个正确的estimatedRowHeight

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

我希望这可以帮助别人。

于 2019-10-29T16:01:44.467 回答
-3

这是100%的工作

change the tableView.reloadData() 

进入

tableView.reloadRows(at: tableView!.indexPathsForVisibleRows!, with: .none)
于 2017-09-22T02:49:51.863 回答
-4

因为它工作正常

[tView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]
             atScrollPosition:UITableViewScrollPositionTop
                     animated:NO];
于 2016-11-29T11:52:35.747 回答