ios 调用-reloadData后如何保持UITableView contentoffset

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8640409/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 16:02:19  来源:igfitidea点击:

How to keep UITableView contentoffset after calling -reloadData

iosuitableviewreloaddatacontentoffset

提问by avincross

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:2which is kind of a hack may be too short or too long, so how can I implement it?

我不知道在reloadData. 使用afterDelay:2哪种 hack 可能太短或太长,那么我该如何实现呢?

采纳答案by Louis

I was recently working with reloadData-- reloadDatadoesn't change the contentOffsetor scroll the table view. It actually stays the same if the offset is less than the new amount of data.

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

回答by Matt Koala

I was having trouble with this because I mess with cell sizing in my cellForRowAtIndexPath method. I noticed that the sizing information was off after doing reloadData, so I realized I needed to force it to layout immediately before setting the content offset back.

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

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

回答by Skywalker

Calling reloadDataon the tableView does not change the content offset. However, if you are using UITableViewAutomaticDimensionwhich was introduced in iOS 8, you could have an issue.

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

While using UITableViewAutomaticDimension, one needs to write the delegate method tableView: estimatedHeightForRowAtIndexPath:and return UITableViewAutomaticDimensionalong with tableView: heightForRowAtIndexPath:which also returns the same.

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

For me, I had issues in iOS 8 while using this. It was because the method estimatedHeightForRowAtIndexPath:method was returning inaccurate values even though I was using UITableViewAutomaticDimension. It was problem with iOS 8 as there was no issue with iOS 9 devices.

对我来说,我在使用它时在 iOS 8 中遇到了问题。这是因为estimatedHeightForRowAtIndexPath:即使我使用UITableViewAutomaticDimension. iOS 8 有问题,因为 iOS 9 设备没有问题。

I solved this problem by using a dictionary to store the value of the cell's height and returning it. This is what I did.

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

- (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;
}

The check for whether height exists is for the first time page loads.

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

回答by Nik Kov

Swift 4 variant of @Skywalker answer:

@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
}


Another solution (fetched from MessageKit):

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

This method should be called instead of reloadData. This can fit for specific cases.

应调用此方法而不是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)
}

回答by Cyril

By default, reloadData keeps the contentOffset. However, it could be updated if you do have inaccurate estimatedRowHeight values.

默认情况下,reloadData 保持 contentOffset。但是,如果您确实有不准确的估计行高度值,则可以更新它。

回答by saranpol

In my case uncheck row height automatic and estimate automatic problem solved

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

fix reload problem

修复重装问题

回答by bingxin xue

If you implement estimatedHeightForRowAtIndexPathmethod and your estimate is not right, you will possible get into this situation.

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

To solve this, you can return a large height that bigger than every cell height in your tableView, like this:

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

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

回答by Bohdan Savych

If you insert data at the beginning of your dataSource array, you need to change contentOffsetlike this: Swift 3+

如果在 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)
}

回答by Mikhail Vasilyev

I had the same issue however none of answers suggested here worked. Here's how i solved it. Subclass UITableViewand override layoutSubviewsmethod like this:

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

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

回答by Andrew Romanov

@Skywalker's answer showed best workaround for estimated height of cells problem. But sometimes problem lyes in a different place.
Sometimes the problem lyes in contentInsets of table view. If you make reload data while tableView is not visible on the screen you can face with wrong offset after the table view appears on the screen.
It happens because UIViewController can control insets if his scrollView when the scrollView is appearing to allow lying of scrollView below transparent navigationBar and statusBar.
I've faced with this behaviour in iOS 9.1

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