ストーリーボードのカスタムセルの行の高さの設定が応答しない


213

テーブルビューのセルの1つのセルの高さを調整しようとしています。問題のセルの「サイズインスペクタ」内の「行の高さ」設定からサイズを調整しています。iPhoneでアプリを実行すると、セルのデフォルトサイズはテーブルビューの「行サイズ」から設定されます。

テーブルビューの「行サイズ」を変更すると、すべてのセルのサイズが変わります。1つのセルにのみカスタムサイズを設定したいので、これは行いません。プログラムで問題を解決する投稿をたくさん見ましたが、可能であれば、ストーリーボードを介して実行することをお勧めします。

回答:


295

動的な細胞、rowHeightのUITableViewのセットは常に、個々の細胞のrowHeightのを上書きします。

ただし、静的セルでrowHeightは、個々のセルに設定するとUITableViewをオーバーライドできます。

それがバグかどうかわからない、Appleが意図的にこれを行っているのだろうか?


36
正解#3、特にこの回答はInterface Builder / Storyboardsに適用されるため。IBでセルを選択した場合、サイズインスペクターは行の高さを上部に表示し(「カスタム」チェックボックスを使用)、テーブルビュー全体を選択した場合、サイズインスペクターは行の高さを上部に表示します(「カスタム」なし) " この場合)。pixelfreakが言うように、動的セルにはテーブルビュー設定のみが使用されます。(意図的かどうかは不明です)
ルバー

8
この答えは、解決策は単にUITableViewコンテンツをからに変更することであることを意味Dynamic PrototypesStatic Cells、私はそれを行いました、そして私のプロジェクト全体が爆発しました..ほとんど自殺しました。
2013年

4
この番号を取得する方法はありますか?深く掘り下げる唯一の方法は、ストーリーボードに飛び込んでそこから引き出すことですか?
Biclops 2013年

テーブルは動的であるため、これは間違いなくバグではありません。システムは、これらの各セルをどこで使用したかをどのようにして知ることができますか?また、各プロトタイプが使用される回数。
Vincent Bernier、2014年

最初にテーブルビューを「静的セル」として設定し、それを「動的プロトタイプ」に変更した場合にも問題があるようです。rowHeightのデリゲートメソッドも無視されるという問題がありました。最初にストーリーボードXMLを直接編集して解決し、最後に最初から動的なプロトタイプを使用して、ストーリーボードシーンをゼロから再構築しました。
Eric Goldberg

84

UITableViewControllerを使用する場合は、このメソッドを実装します。

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

行の機能では、高さを選択できます。例えば、

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

この例では、最初の行の高さは100ピクセルで、その他は60ピクセルです。

これがお役に立てば幸いです。


2
いずれにせよ、常に両方を行う必要がありますか(カスタムをチェックしてストーリーボードの行の高さの値を編集して、heightForRowAtIndexPathで指定します)
marciokoko 2012

これについて私を助けてくれませんか?動的な高さの動的なセルが必要ですが、この方法を使用すると、すべてのセルが何を返すかに関係なく、代わりに表示されなくなります。iPhone 5を除いて、デバイスは期待どおりに動作します。理由がわかりますか?
オストメイストロ2017年

34

ダイナミックセルの場合、rowHeightonを設定するとUITableView、個々のセルが常にオーバーライドされますrowHeight

この動作はIMOのバグです。2つの場所でUIを管理する必要がある場合はいつでも、エラーが発生しやすくなります。たとえば、ストーリーボードでセルサイズを変更する場合は、セルでも変更することを忘れないでくださいheightForRowAtIndexPath:。Appleがバグを修正するまで、現在の最善の回避策はオーバーライドheightForRowAtIndexPath:することですが、マジックナンバーを使用するのではなく、ストーリーボードの実際のプロトタイプセルを使用して高さを決定します。次に例を示します。

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

これにより、プロトタイプセルの高さの変更が実行時に自動的に取得され、UIをストーリーボードという1か所で管理するだけで済みます。


2
キャッシュを含む完全なコードを含む回答を投稿しました。stackoverflow.com/a/16881312/292166を
JosephH

1
すごいすごい!私は答えを賛成しましたが、気をつけてください!このメソッドは、コメントで@Brennanが述べたように、パフォーマンスを低下させるだけでなく、reloadDataごとにメモリリークなどのメモリ割り当てを増加させます!上記のlensovetの回避策を使用する必要があります!このメモリリークをキャッチするために1日を過ごしてください!
スカイワインダー2014年

cell.bounds.size.heightは常に0.0を返すため、Swiftでは機能しません
King-Wizard

1
システムがtableView:heightForRowAtIndexPathメソッドを呼び出した時点では、セルはまだ存在していません。渡されたindexPathを使用してデータモデルにインデックスを付け、そこで使用されるセルの種類を確認し、そのセルの高さを計算して返すことができるはずです。これにより、システムはセルを作成する前にテーブル内のセルをレイアウトできます。
King-Wizard

1
また、独自のcellForRowAtIndexPathメソッドを呼び出さないでください。テーブルビューには、現在画面に表示されている場合にそのインデックスパスのセルを返すcellForRowAtIndexPathメソッドがありますが、多くの場合、そのインデックスパスにはセルが関連付けられていません。
King-Wizard

22

プロトタイプセルを使用するストーリーボードで機能するように、さまざまな回答/コメントのヒントとなるコードを作成しました。

このコード:

  • セルの高さをストーリーボードの明らかな場所以外に設定する必要はありません
  • パフォーマンス上の理由から高さをキャッシュします
  • 共通の関数を使用してインデックスパスのセル識別子を取得し、ロジックの重複を回避します

Answerbot、Brennan、lensovetに感謝します。

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>

私はこれが古い答えであることを知っていますが、他の誰かがこれがスタックオーバーフローを引き起こすことを知っていますか?ビューが読み込まれると、UISectionRowData refreshWithSection:tableViewRowData:が呼び出され、tableView:heightForRowAtIndexPath:がdequeueReusableCellWithIdentifierを呼び出し、[UISectionRowData ...]が呼び出されるため、再帰的なスタックオーバーフローが発生します。私は本当にこのソリューションを使いたかったのですが、iOS7では動作しないようです。
sbaker、2014年

cell.bounds.size.heightは常に0.0を返すため、Swiftでは機能しません
King-Wizard

システムがtableView:heightForRowAtIndexPathメソッドを呼び出した時点では、セルはまだ存在していません。渡されたindexPathを使用してデータモデルにインデックスを付け、そこで使用されるセルの種類を確認し、そのセルの高さを計算して返すことができるはずです。これにより、システムはセルを作成する前に、テーブル内のセルをレイアウトできます。
King-Wizard

また、独自のcellForRowAtIndexPathメソッドを呼び出さないでください。テーブルビューには、現在画面に表示されている場合にそのインデックスパスのセルを返すcellForRowAtIndexPathメソッドがありますが、多くの場合、そのインデックスパスにはセルが関連付けられていません。
King-Wizard

@ snow-tiger私はこれをすぐに試したことはありませんが、あなたのコメントは理解できません。私は[[my] own cellForRowAtIndexPath method]を呼び出さないでください。また、呼び出さないようにしています。また、tableView:heightForRowAtIndexPathが呼び出されたときにセルが存在しないことも知っています。それがこのコードの要点であり、なぜdequeueReusableCellWithIdentifierを使用してセルを取得するのですか。この回答に掲載されているコードは機能します。Swiftで何か特別なことが行われているか、Swiftの変換で正しくないものがあるか、またはこの質問/回答が対象としている別の問題を解決しようとしています。
JosephH

19

行の高さに変更する必要のある場所は実際には2つあります。最初にセル(既に変更済み)と、テーブルビューを選択し、サイズインスペクターを確認します。


テーブルビュー(セルビューではない)>サイズインスペクタータブ>行の高さ
iman kazemayni '26 / 12/26

私はこの詳細を忘れるたびに私を狂わせます。ストーリーボードを使用しているときにセルを調整してもtableViewが自動的に更新されない理由はわかりません!
スクーター

11

バグだと思います。

ユーティリティインスペクタではなく、ストーリーボード上で直接マウスをドラッグして高さを調整してみてください。

私はこの方法でこの問題を解決しました。


10

swiftを使用している場合は、次のように使用します。ストーリーボードを使用して行の高さを選択しないでください。プログラムでこのようにテーブルの行の高さを設定し、

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}

この作品が、より一般的にあなたが行うことができます:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
アンディ・チョウ

7

次の行を使用して、ストーリーボードからUITableviewCell(UITableviewController-静的セル内)の高さを取得できます。

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

    return height;
}

6

XMLビューでストーリーボードを開き、rowHeight必要な要素の属性を編集してみます。

プロトタイプの行にカスタムのrowHeightを設定しようとしたときにうまくいきました。インスペクター経由では機能しませんが、XML経由では機能します。


1
右クリックして「名前を付けて開く」->「ソースコード」を選択しました。rowHeightはすでに120に設定されています。ストーリーボードにはバグがあり、カスタムセルの高さは無視されると思います。
zirinisp 2012年

6

cellsカスタムのプロトタイプを使用して、その。:をheight呼び出しcellForRowAtIndexPath:て返すことができますframe.height

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}

このメソッドの追加は非常にうまく機能し、現在はすべてのカスタマイズをストーリーボードに含める必要があります。
ダスピアニスト2013年

これまでで最高のソリューション
TheJeff

4

静的な行の高さを設定したい場合は、次のようにすることができます。

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

4

私は最近これに取り組んでいます。私の問題は、上記のheightForRowAtIndexPath:方法を使用したソリューションがシミュレーターのiOS 7.1で機能するが、iOS 8.1に切り替えるだけで結果が完全に台無しになったことでした。

セルフサイジングセルの詳細を読み始めました(iOS 8で導入されました。こちらをお読みください)。の使用がUITableViewAutomaticDimensioniOS 8で役立つことが明らかでした。そのテクニックを使用してみましたが、使用を削除しましたheightForRowAtIndexPath:が、iOS 8では完全に機能していました。しかし、iOS 7はそうではありませんでした。私は何をしましたか?heightForRowAtIndexPath:iOS 8ではなくiOS 7が必要でした。

上記の@JosephHの回答から借用した私のソリューション(簡潔にするために切り詰めたもの)は次のとおりです。

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
        static NSMutableDictionary *heightCache;
        if (!heightCache)
            heightCache = [[NSMutableDictionary alloc] init];
        NSNumber *cachedHeight = heightCache[cellIdentifier];
        if (cachedHeight)
            return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@ "8.0")は、実際には、どこかで見つけた(非常に役立つ)使用しているマクロ定義のセットからのものです。それらは次のように定義されます。

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

2

Swift 4を使用してXCode 9で作業しているときに同じ問題が発生しました。

Cell内のUI要素にAutoLayout追加すると、カスタムセルの行の高さが指定どおりに機能します。


1
こんにちは、どうやってそれをしましたか?
バックパッカー2018年

セルの下部に制約セットを追加するだけです。
ジャスティン

これを実行して、セルの高さに対してストーリーボードで「自動」を選択すると、実行すると正しく表示されますが、ストーリーボードですべての高さを44に設定しようとしています。ストーリーボードを正しく表示するにはどうすればよいですか?
David

1

動的セルの場合、UITableViewに設定されたrowHeightは、常に個々のセルのrowHeightをオーバーライドします。行内のコンテンツの場合、動的な高さを計算するだけです。


0

私が見つけることができた唯一の本当の解決策はこれです

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

これは機能し、StoryBoardにすでにロジックを複製している場合に、恐ろしい切り替えを行わないようにすることができます。パフォーマンスについてはcellForRow:わかりませんが、すでに初期化されているセルに到着すると、それは同じくらい速いと思います。もちろん、ここに付随的な損害があると思われますが、ここでは問題なく機能しているようです。

私もここにこれを投稿しました:https : //devforums.apple.com/message/772464

EDIT:Ortwin Gentzはそのことを思い出したheightForRowAtIndexPath:ために呼び出されるすべての目に見えるものだけではなく、テーブルビューのセル。iOSは正しいスクロールバーを表示できるようにするために全体の高さを知る必要があるため、論理的に聞こえます。これは、小さなTableView(20セルなど)ではおそらく問題ないが、1000セルのTableViewではそれを忘れることを意味します。

また、XMLに関する以前のトリック:最初のコメントと同じです。正しい値はすでにそこにありました。


0

コメントとして追加されましたが、可視性のための回答として投稿します:

最初にテーブルビューを「静的セル」として設定し、それを「動的プロトタイプ」に変更した場合にも問題があるようです。のデリゲートメソッドでさえheightForRowAtIndexPath無視されるという問題がありました。最初にストーリーボードXMLを直接編集して解決し、最後に最初から動的なプロトタイプを使用して、ストーリーボードシーンをゼロから再構築しました。


0

Interface Builderを介してこの問題の解決策が見つからなかったため、最初の質問でInterface Builderを介した解決策を求めていたとしても、2つの動的セルを使用してSwiftの問題にプログラムによる解決策を投稿することにしました。とにかく、スタックオーバーフローコミュニティに役立つと思います。

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }

これはすべて問題ありませんが、コードを誤解しない限り、マジックナンバーに依存し、ダイナミックセルのIBに設定した値を完全に無視するので、OPの質問に対する正確な解決策ではありません
Danny

0

他にできることの1つは、ドキュメントアウトラインに移動し、プロトタイプセルがネストされているテーブルビューを選択することです。次に、サイズインスペクターで、テーブルビューの行の高さを目的の値に変更し、[自動]チェックボックスをオフにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.