27

UITableViewCell のカスタム サブクラスを持つアプリケーションに取り組んでいます。セル内のテキストに基づいてセルの高さを動的にしたい。私は heightForRowAtIndexPath メソッドでそれをやろうとしています。しかし、私はいくつかの問題を抱えています。次のコードは EXC_BAD_ACCESS(code=2 address=0xb7ffffcc) エラーを引き起こします。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{

    PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];

    CGSize postTextSize;
    if (![cell.postText.text isEqualToString:@""]) {
        postTextSize = [cell.postText.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(cell.postText.frame.size.width, 200)];
    } else {
        postTextSize = CGSizeMake(cell.postText.frame.size.width, 40);
    }

    return postTextSize.height + cell.userName.frame.size.height + 10;
}

私が代わりに持っている場合:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    return 100;
}

できます。

この行でクラッシュしているようです: PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];、理由はわかりません。シミュレーターをクリーンにビルドしてリセットしようとしましたが、どちらも機能しませんでした。なぜこれが起こっているのですか?

4

5 に答える 5

54

問題を再現しようとしました。cellForRowAtIndexPath:内部で呼び出すと、 が再帰的に呼び出されることがわかりましたheightForRowAtIndexPathheightForRowAtIndexPath以下は、3 つの再帰ステップ後のスタック バックトレースの抜粋です。

frame #0: 0x000042d0 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 48 at DITableViewController.m:262
frame #1: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #2: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #3: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #4: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42
frame #5: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177
frame #6: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113
frame #7: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262
frame #8: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #9: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #10: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #11: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42
frame #12: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177
frame #13: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113
frame #14: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262
frame #15: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437
frame #16: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144
frame #17: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137
frame #18: 0x003ff66d UIKit`-[UITableView noteNumberOfRowsChanged] + 119
frame #19: 0x003ff167 UIKit`-[UITableView reloadData] + 764

最後にプログラムがクラッシュします。私のシミュレータでは、これはバック トレースが約 57000 レベルの深さのときに発生します。


古い回答(間違っていませんが、EXC_BAD_ACCESS について説明していません):

問題はそれです

[tableView cellForRowAtIndexPath:indexPath]

nil現在表示されていない行を返します。テーブル ビューは、現在表示されている行を表示するために必要な数のセルのみを割り当てます。テーブル ビューをスクロールすると、セルが再利用されます。

ただし、行が表示される前に、テーブル ビューのすべてのセルに対してheightForRowAtIndexPath呼び出されます。

結果として、テーブル ビュー セルからテキストを取得して で高さを計算するべきではありませんheightForRowAtIndexPath。代わりに、データ ソースからテキストを取得する必要があります。

于 2012-09-29T13:42:46.280 に答える
7

tableView heightForRowAtIndexPathbefore が呼び出されcellForRowAtIndexPath、セルが表示される前に、最初に高さを計算する必要があります。

textからではなく、データソースから取得する必要がありますcell

于 2012-09-29T15:26:33.260 に答える
2

私は同様の問題を抱えていて、私が今していることを共有するために出くわしました。

CGFloat mycell_height;

- (void)viewDidLoad
{
    [super viewDidLoad];
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"];
    mycell_height = cell.frame.size.height;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
  return mycell_height; //assuming all cells are the same
}

これが、これを探していて、私のような iOS プログラミングの初心者に役立つことを願っています :-)

于 2014-03-28T12:40:31.213 に答える
0

この場合、tableView: heightForRowAtIndexPath: が最初に呼び出された時点で tableView にセルがないため、UITableView の cellForRowAtIndexPath: メソッドではなく、indexPath のセルを取得するために UITableViewDataSource メソッドを呼び出す必要があります。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath {
    PostCell *cell = (PostCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];

    CGSize postTextSize;
    if (![cell.postText.text isEqualToString:@""]) {
    postTextSize = [cell.postText.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(cell.postText.frame.size.width, 200)];
    } else {
        postTextSize = CGSizeMake(cell.postText.frame.size.width, 40);
    }

    return postTextSize.height + cell.userName.frame.size.height + 10;
}

それは機能し、EXC_BAD_ACCESS 例外は発生しません。

于 2015-04-13T06:51:38.413 に答える
-1

迅速:

これを使用して、テーブルがいつロードされたかを確認します。

extension UITableView {
    func reloadData(completion: ()->()) {
        UIView.animateWithDuration(0, animations: { self.reloadData() })
            { _ in completion() }
    }
}

ブール値を作成する

var tables_loaded = Bool(false)

次にviewDidLoadで:

tableView.reloadData {
    tables_loaded = true
    // call functions or other stuff regarding table manipulation.
    // tableView.beginUpdates()
    // tableView.endUpdates()
}

次に

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
            if tables_loaded {
                let cell = tableView.cellForRowAtIndexPath(indexPath)
                 // Do your magic here!
             }

}
于 2015-04-19T16:53:36.233 に答える