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

の後に呼び出されるデリゲートメソッドを知りませんreloadData。そして、afterDelay:2どちらの種類のハックを使用するかは短すぎるか長すぎる可能性がありますが、どうすればそれを実装できますか?

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

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

を使用している間UITableViewAutomaticDimension、デリゲートメソッドを記述して戻る必要がありtableView: estimatedHeightForRowAtIndexPath:ます。これも同じものを返します。UITableViewAutomaticDimensiontableView: heightForRowAtIndexPath:

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

辞書を使用してセルの高さの値を格納し、それを返すことで、この問題を解決しました。これが私がしたことです。

- (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のSwift5バリアントの回答:

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

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

override func layoutSubviews() {
    let offset = contentOffset
    super.layoutSubviews()
    contentOffset = offset
}
于 2016-08-24T02:15:39.453 に答える
2

@Skywalkerの回答は、セルの高さの推定問題に対する最善の回避策を示しています。しかし、問題が別の場所にある場合もあります。
テーブルビューのcontentInsetsに問題がある場合があります。tableViewが画面に表示されていないときにデータをリロードすると、テーブルビューが画面に表示された後、間違ったオフセットに直面する可能性があります。
これは、scrollViewが表示されているときにUIViewControllerがscrollViewを制御して、透明なナビゲーションバーとステータスバーの下にscrollViewを配置できるようにするために発生します。
私はiOS9.1でこの動作に直面しました

于 2016-12-09T04:12:56.993 に答える
1

上記のマットの答えは、それがの問題でなければならないことに気づきましたestimatedRowHeight

したがって、いくつかの指摘されているように、正しいを設定するためだけにを設定するときreloadDataは、変更しないでください。contentOffsetrowHeight = UITableView.automaticDimensionestimatedRowHeight

estimatedRowHeightが推定よりも短い場合UITableView、高さを修正しようとし、スクロール動作が表示されますが、高さの問題があるセルが表示されている場合に限ります。つまり、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 に答える