web-dev-qa-db-ja.com

-reloadDataを呼び出した後にUITableView 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を使用するのは短すぎたり長すぎたりする可能性があります。

56
avincross

私は最近reloadDataを使っていました-reloadDatacontentOffsetを変更せず、テーブルビューをスクロールしません。オフセットが新しいデータ量より少ない場合、実際には同じままです。

14
Louis

CellForRowAtIndexPathメソッドでセルのサイズを変更するため、これで問題が発生していました。 reloadDataの実行後にサイズ情報がオフになっていることに気づいたので、コンテンツオフセットを設定する直前にレイアウトを強制する必要があることに気付きました。

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

TableViewでreloadDataを呼び出しても、コンテンツオフセットは変更されません。ただし、iOS 8で導入されたUITableViewAutomaticDimensionを使用している場合は、問題が発生する可能性があります。

UITableViewAutomaticDimensionを使用している間、デリゲートメソッドtableView: estimatedHeightForRowAtIndexPath:を記述し、tableView: heightForRowAtIndexPath:とともにUITableViewAutomaticDimensionを返す必要があります。

私にとって、これを使用している間にiOS 8で問題が発生しました。 UITableViewAutomaticDimensionを使用していても、メソッドestimatedHeightForRowAtIndexPath:メソッドが不正確な値を返していたためです。 iOS 9デバイスでは問題がなかったため、iOS 8では問題でした。

辞書を使用してセルの高さの値を保存して返すことで、この問題を解決しました。これは私がやったことです。

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

高さが存在するかどうかのチェックは、ページが初めてロードされるときです。

86
Skywalker

@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)
}
30
Nik Kov

デフォルトでは、reloadDataはcontentOffsetを保持します。ただし、不正確なtimatedRowHeight値がある場合は更新される可能性があります。

23
Cyril

私の場合、行の高さの自動チェックを外し、解決された自動問題を推定します

fix reload problem

18
saranpol

estimatedHeightForRowAtIndexPathメソッドを実装し、推定が正しくない場合、この状況に陥る可能性があります。

これを解決するには、次のように、tableViewのすべてのセルの高さよりも大きな大きな高さを返すことができます。

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

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)
}
7
Bohdan Savych

@Skywalkerの答えは、セルの高さの推定問題に対する最善の回避策を示しました。しかし、時には別の場所で問題を起こします。
テーブルビューのcontentInsetsで問題が解決する場合があります。テーブルビューが画面に表示されていないときにデータをリロードすると、テーブルビューが画面に表示された後に間違ったオフセットに直面する可能性があります。

iOS 9.1でこの動作に直面しました

2
Andrew Romanov

私は同じ問題を抱えていましたが、ここで提案された答えはどれもうまくいきませんでした。ここに私がそれを解決した方法があります。 UITableViewをサブクラス化し、layoutSubviewsメソッドを次のようにオーバーライドします。

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