51

いくつかの異なるセクションを持つ UITableView があります。1 つのセクションには、ユーザーがテキストを UITextView に入力するとサイズが変更されるセルが含まれています。別のセクションには、HTML コンテンツをレンダリングするセルが含まれており、高さの計算に比較的コストがかかります。

現在、ユーザーが UITextView に入力すると、テーブル ビューでセルの高さを更新するために、

[self.tableView beginUpdates];
[self.tableView endUpdates];

ただし、これにより、実際には入力された単一のセルのみを更新する必要がある場合に、テーブル内のすべてのセルの高さが再計算されます。それだけでなく、 を使用して推定高さを再計算する代わりに、表示されていないセルも含めてすべてのセルを呼び出します。tableView:estimatedHeightForRowAtIndexPath:tableView:heightForRowAtIndexPath:

この不要な作業をすべて行わずに、単一のセルの高さだけを更新するようにテーブル ビューに要求する方法はありますか?

アップデート

私はまだこれに対する解決策を探しています。提案どおり、 を使用してみましreloadRowsAtIndexPaths:たが、うまくいかないようです。reloadRowsAtIndexPaths:1 行でも呼び出すと、要求した行に対してのみ呼び出されますがheightForRowAtIndexPath:、すべての行に対して呼び出されます。cellForRowAtIndexPath:実際、行が挿入、削除、または再ロードさheightForRowAtIndexPath:れるたびに、テーブル セルのすべての行に対して呼び出されるように見えます。

willDisplayCell:forRowAtIndexPath:また、セルが表示される直前に高さを計算するコードを入れてみました。これを機能させるには、計算を行った後にテーブル ビューに行の高さを再要求させる必要があります。残念ながら、[self.tableView beginUpdates]; [self.tableView endUpdates];fromを呼び出すとwillDisplayCell:forRowAtIndexPath:、UITableView の内部コードの奥深くで、範囲外のインデックス例外が発生します。彼らは私たちがこれをすることを期待していないと思います。

[self.tableView endUpdates]表示されていないセルを呼び出さないのは、SDK のバグのように感じずにはいられませんestimatedHeightForRowAtIndexPath:が、何らかの回避策を見つけようとしています。どんな助けでも大歓迎です。

4

8 に答える 8

33

前述のようにreloadRowsAtIndexPaths:withRowAnimation:、テーブル ビューが新しいセル ビューを要求するだけで、更新されたセルの高さをUITableViewDataSource要求することはありません。UITableViewDelegate

残念ながら、高さは次の呼び出しによってのみ更新されます。

[tableView beginUpdates];
[tableView endUpdates];

2 つの呼び出しの間に変更がなくても。

高さを計算するアルゴリズムに時間がかかりすぎる場合は、それらの値をキャッシュする必要があります。何かのようなもの:

- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat height = [self cachedHeightForIndexPath:indexPath];

    // Not cached ?
    if (height < 0)
    {
        height = [self heightForIndexPath:indexPath];
        [self setCachedHeight:height
                 forIndexPath:indexPath];
    }

    return height;
}

-1そして、コンテンツが変更されたとき、または初期化時にそれらの高さを必ずリセットしてください。

編集:

また、高さの計算を可能な限り (スクロールされるまで) 遅らせたい場合は、これを実装してみてください (iOS 7+ のみ):

@property (nonatomic) CGFloat estimatedRowHeight

行の高さの非負の見積もりを提供すると、テーブル ビューの読み込みのパフォーマンスを向上させることができます。テーブルに高さが可変の行が含まれている場合、テーブルの読み込み時にすべての高さを計算するとコストがかかる可能性があります。推定を使用すると、ロード時間からスクロール時間までのジオメトリ計算のコストの一部を延期できます。

デフォルト値は 0 です。これは、見積もりがないことを意味します。

于 2013-10-23T08:40:26.133 に答える
26

このバグは iOS 7.1 で修正されました。

iOS 7.0 では、この問題を回避する方法はないようです。を呼び出すと、テーブル[self.tableView endUpdates]heightForRowAtIndexPath:のすべてのセルに対して呼び出されます。

ただし、iOS 7.1 では、 を呼び出すと、表示されているセルに対して[self.tableView endUpdates]heightForRowAtIndexPath:呼び出され、表示されていないセルに対してestimatedHeightForRowAtIndexPath:が呼び出されます。

于 2014-03-11T02:16:10.550 に答える
8

可変の行の高さは、テーブル ビューのパフォーマンスに非常に悪い影響を与えます。一部のセルに表示される Web コンテンツについて話しています。何千もの行について話しているのでなければ、UITableView の代わりにUIWebViewを使用してソリューションを実装することを検討する価値があるかもしれません。同様の状況があり、カスタム生成された HTML マークアップを使用して UIWebView を使用したところ、見事に機能しました。おそらくご存じのとおり、Web コンテンツを含む動的セルがあると、厄介な非同期の問題が発生します。

  • セルの内容を設定した後、する必要があります
  • セル内の Web ビューが Web コンテンツのレンダリングを完了するまで待ちます。
  • 次に、UIWebView に移動し、JavaScript を使用して、HTML ドキュメントの高さを確認する必要があります。
  • 次に、UITableViewCell の高さを更新します。

ユーザーにとってはまったく楽しくなく、たくさんのジャンプとジッタリングが発生します。

UITableView を使用する必要がある場合は、計算された行の高さを確実にキャッシュしてください。そうすれば、 で返却する方が安くなりますheightForRowAtIndexPath:。UITableView に何をすべきかを伝える代わりに、データ ソースを高速化します。

于 2013-10-23T14:20:37.087 に答える
0

このメソッドheightForRowAtIndexPath:は常に呼び出されますが、私が提案する回避策を次に示します。

ユーザーが を入力しているときはいつでも、セルUITextViewをローカル変数に保存します。次に、が呼び出されたときに、保存された の値を確認します。保存されたセルがそうでない場合は、サイズを変更する必要があるセルを取得して実行します。他のセルについては、キャッシュされた値を使用します。保存されている場合は、通常のコード行を実行しますが、これはあなたの場合は要求が厳しいものです。indexPathheightForRowAtIndexPath:indexPathindexPathnilindexPathnil

これを行うことをお勧めする方法は次のとおりです。

のプロパティtagを使用して、UITextViewサイズ変更が必要な行を追跡します。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ...

    [textView setDelegate:self];
    [textView setTag:indexPath.row];

    ...
}

次に、UITextViewデリゲートのメソッドtextViewDidChange:で、 を取得しindexPathて保存します。savedIndexPathローカル変数です。

- (void)textViewDidChange:(UITextView *)textView
{
    savedIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0];
}

最後に、 の値を確認し、savedIndexPath必要なものを実行します。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (savedIndexPath != nil) {
        if (savedIndexPath == indexPath.row) {
            savedIndexPath = nil;

            // return the new height
        }
        else {

            // return cached value
        }
    }
    else {
        // your normal calculating methods...
    }
}

これが役立つことを願っています!幸運を。

于 2013-10-27T03:49:42.373 に答える
0

問題を回避する方法を考え出すことになりました。レンダリングする必要がある HTML コンテンツの高さを事前に計算し、その高さをコンテンツと共にデータベースに含めることができました。そうすれば、任意のセルの高さを更新するときにすべてのセルの高さを提供することを余儀なくされますが、高価な HTML レンダリングを行う必要がないため、非常に機敏です。

残念ながら、このソリューションは、すべての HTML コンテンツを事前に用意している場合にのみ機能します。

于 2013-12-20T06:22:44.237 に答える