繁体   English   中英

调用-reloadData后如何保持UITableView contentoffset

[英]How to keep UITableView contentoffset after calling -reloadData

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这是一种黑客可能太短或太长,那么我该如何实现呢?

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

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

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

在使用UITableViewAutomaticDimension ,需要编写委托方法tableView: estimatedHeightForRowAtIndexPath:并返回UITableViewAutomaticDimensiontableView: heightForRowAtIndexPath: ,它也返回相同的。

对我来说,我在使用它时在 iOS 8 中遇到了问题。 这是因为即使我使用了UITableViewAutomaticDimension ,方法estimatedHeightForRowAtIndexPath:方法也返回了不准确的值。 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;
}

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

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

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

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

修复重装问题

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

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

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

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

如果在 dataSource 数组的开头插入数据,则需要像这样更改contentOffsetSwift 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)
}

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

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

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

马特上面的回答让我意识到它必须是estimatedRowHeight高度的问题。

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

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

我希望这可以帮助别人。

这是工作 100%

change the tableView.reloadData() 

进入

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

因为它工作正常

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM