ストーリーボードでカスタムテーブルビューセクションのヘッダーとフッターを実装する方法


176

ストーリーボードを使用せずUIViewに、キャンバスにをドラッグしてレイアウトし、それをtableView:viewForHeaderInSectionor tableView:viewForFooterInSectionメソッドまたはdelegateメソッドに設定するだけで済みます。

UIViewをキャンバスにドラッグできないStoryBoardでこれをどのように達成しますか

回答:


90

この質問はiOS 5に関するものでしたが、将来の読者のためdequeueReusableHeaderFooterViewWithIdentifierに、今ではの代わりに効果的なiOS 6を使用できることに注意してくださいdequeueReusableCellWithIdentifier

では、またはのviewDidLoadいずれregisterNib:forHeaderFooterViewReuseIdentifier:かを呼び出しますregisterClass:forHeaderFooterViewReuseIdentifier:。次に、viewForHeaderInSectionに電話しますtableView:dequeueReusableHeaderFooterViewWithIdentifier:。このAPI(NIBベースのビューまたはプログラムで作成されたビューのいずれか)でセルプロトタイプを使用しませんが、これはデキューされたヘッダーとフッター用の新しいAPIです。


13
ため息 NIBの代わりに、プロト細胞の使用に関するこれら二つのきれいな答えが5票中であることを恥を、そして「プロト細胞とそれをハック」答えは200の上まである
Benjohn

5
違いは、Tiemeのハックを使用すると、同じストーリーボードでデザインを作成でき、個別のXibを使用しないことです
CedricSoubrie

どういうわけか、個別のNIBファイルの使用を避け、代わりにストーリーボードを使用できますか?
Foriger

@Foriger-現時点ではありません。ストーリーボードのテーブルビューでは奇妙な省略ですが、それはそれです。必要に応じて、プロトタイプセルを使用してその粗雑なハックを使用しますが、個人的には、ヘッダー/フッタービューのNIBの煩わしさに我慢しました。
Rob

2
「古い」というだけでは物足りないんですけどね。受け入れられた答えは、ヘッダービューとフッタービューのプロトタイプセルを作成できないという事実を回避するための巧妙な方法です。しかし、ヘッダーとフッターのデキューがセルのデキューと同じように機能することを前提としているので、それは単に間違っていると思います(ヘッダー/フッター用の新しいAPIの存在がそうではない可能性があることを示唆しています)。受け入れられた答えは、iOS 5では理解できた創造的な回避策ですが、iOS 6以降では、ヘッダー/フッターの再利用のために明示的に設計されたAPIを使用するのが最善です。
Rob

384

プロトタイプセルをセクションヘッダーやフッターとして使用するだけです。

  • 余分なセルを追加し、そこに必要な要素を配置します。
  • 識別子を特定の何かに設定します(私の場合はSectionHeader)
  • 実装tableView:viewForHeaderInSection:方法やtableView:viewForFooterInSection:方法を
  • [tableView dequeueReusableCellWithIdentifier:]ヘッダーを取得するために使用
  • tableView:heightForHeaderInSection:メソッドを実装します。

(screenhotを参照)

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    static NSString *CellIdentifier = @"SectionHeader"; 
    UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (headerView == nil){
        [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"];
    }
    return headerView;
}  

編集:ヘッダーのタイトルを変更する方法(コメント付き質問):

  1. ヘッダーセルにラベルを追加する
  2. ラベルのタグを特定の番号(123など)に設定します
  3. あなたのtableView:viewForHeaderInSection:メソッドで次を呼び出すことによってラベルを取得します:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. これで、ラベルを使用して新しいタイトルを設定できます。
    [label setText:@"New Title"];

7
パーティーには遅れますが、セクションヘッダービューでのクリックを無効にするには、viewForHeaderInSection メソッドでcell.contentViewを返すだけです(カスタムUIViewを追加する必要はありません)。
paulvs 2014年

3
@PaulVon私は最新のプロジェクトでそれを行おうとしましたが、そうする場合は、ヘッダーの1つを長押ししようとするとクラッシュします
Hons

13
iOS 6.0でdequeueReusableHeaderFooterViewWithIdentifier導入され、現在はこの回答よりも優先されています。しかし、それを正しく使用するには、さらに多くの手順が必要になります。ガイドはsamwize.com/2015/11/06/…にあります。
samwize 2015年

3
UITableViewCellsを使用してsectionHeaderViewsを作成しないでください。予期しない動作が発生します。代わりにUIViewを使用してください。
AppreciateIt 2016年

4
UITableViewCellヘッダービューとして使用しないでください。視覚的な不具合をデバッグするのは非常に難しくなります。セルがデキューされる方法が原因でヘッダーが消えることがありUITableViewCellUITableViewヘッダーに属していないことに気付くまで何時間かを探します。
raven_raven 2017

53

iOS 6.0以降では、新しいdequeueReusableHeaderFooterViewWithIdentifierAPIで変更が加えられています。

私はガイドを書きました(iOS 9でテスト済み)。

  1. サブクラス UITableViewHeaderFooterView
  2. サブクラスビューでNibを作成し、ヘッダー/フッターに他のすべてのビューを含む1つのコンテナービューを追加します。
  3. にペン先を登録する viewDidLoad
  4. 実装viewForHeaderInSectiondequeueReusableHeaderFooterViewWithIdentifierてヘッダー/フッターを取得するために使用する

2
ハックではないこの答え、そして物事を行う正しい方法をありがとう。また、UITableViewHeaderFooterVIewを紹介してくれてありがとう。ところで、ガイドは素晴らしいです!:)
Roboris

ようこそ。テーブルビューまたはコレクションビューにヘッダー/フッターを実装することは、Appleによって十分に文書化されていません。ちょうど昨日、storyboadを介してデザインしているときに表示されるゲッターヘッダーに行き詰まりました。
samwize 2016

1
これは確かに最高評価の答えになるはずです!
ベンウィリアムズ

xibの代わりに、ストーリーボードを使用してその方法を説明できますか?その場合、デキューは成功しましたが、UILabelsがnullです。
バンカーダイブ2017年

22

ストーリーボードのプロトタイプセルを使用してiOS7で機能させました。カスタムセクションヘッダービューに、ストーリーボードに設定されているセグエをトリガーするボタンがあります。

Tiemeのソリューションから始める

pedro.mが指摘するように、これの問題は、セクションヘッダーをタップすると、セクションの最初のセルが選択されることです。

Paul Vonが指摘するように、これはセル全体ではなくセルのcontentViewを返すことで修正されます。

ただし、Honsが指摘しているように、上記のセクションヘッダーを長押しすると、アプリがクラッシュします。

解決策は、contentViewからすべてのジェスチャ認識を削除することです。

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
     static NSString *CellIdentifier = @"SectionHeader";
     UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

     while (sectionHeaderView.contentView.gestureRecognizers.count) {
         [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]];
     }

     return sectionHeaderView.contentView; }

セクションヘッダービューでジェスチャーを使用していない場合は、この小さなハックでうまくいくようです。


2
あなたが正しい。私はそれとその動作をテストしただけなので、私の研究中に見つけたすべての可能な解決策を含む私の投稿(hons82.blogspot.it/2014/05/uitableviewheader-done-right.html)を更新しました。この修正のためにたくさんありがとう
Hons

良い回答...ありがとうMan
Jogendra.Com

13

ストーリーボードを使用する場合は、テーブルビューのプロトタイプセルを使用してヘッダービューをレイアウトできます。一意のIDとviewForHeaderInSectionを設定すると、そのIDを持つセルをデキューして、UIViewにキャストできます。


12

これのSwift実装が必要な場合は、受け入れられた回答の指示に従い、UITableViewControllerに次のメソッドを実装します。

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

2
何らかの理由で、heightForHeaderInSectionもオーバーライドするまで機能しませんでした。
Entalpi 2016年

これはかなり古いコードです。別の答えを投稿してください!
JZ。

heightForHeaderInSectionをオーバーライドする必要があることを知らなかったので、行き詰まりました。ありがとう@Entalpi
guijob 16

1
@JZ。Swift 3の例では、次のself.tableView.dequeueReusableHeaderFooterViewとSwift 2でデキューします。self.tableView.dequeueReusableCellWithIdentifierには違いがありますか?
Vrutin Rathod 2016

1
これは私のお尻を救った!headerCellに高さを追加すると、表示されます。
CRey

9

私が思いついたソリューションは、ストーリーボードの導入前に使用されたものと基本的に同じです。

新しい空のインターフェースクラスファイルを作成します。UIViewをキャンバスにドラッグし、必要に応じてレイアウトします。

nibを手動でロードし、viewForHeaderInSectionまたはviewForFooterInSectionデリゲートメソッドの適切なヘッダー/フッターセクションに割り当てます。

Appleがストーリーボードを使用してこのシナリオを簡略化し、より良いまたはより簡単な解決策を探し続けていることを願っていました。たとえば、カスタムテーブルのヘッダーとフッターは簡単に追加できます。


ストーリーボードセルをヘッダーメソッドとして使用した場合、アップルはうまくいきました:) stackoverflow.com/questions/9219234/#11396643
Tieme

2
ただし、静的セルではなく、プロトタイプセルに対してのみ機能します。
Jean-Denis Muys 2013年

1
本当に簡単な解決策の1つは、Pixateを使用することです。ヘッダーへの参照を取得しなくても、かなり多くのカスタマイズを行うことができます。実装する必要がtableView:titleForHeaderInSectionあるのは、ワンライナーであるです。
John Starr Dewar 2013

5
それが本当の解決策です...残念ながら上にありませんので、それを見つけるのにしばらく時間がかかりました。私がhons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons

ドラッグアンドドロップするだけで簡単です。
Ricardo 14

5

セルのcontentViewを返すと、2つの問題が発生します。

  1. ジェスチャーに関連するクラッシュ
  2. contentViewを再利用しない(viewForHeaderInSection呼び出しのたびに新しいセルを作成する)

解決:

テーブルヘッダー\フッターのラッパークラス。UITableViewHeaderFooterViewセルを保持するから継承された単なるコンテナです

https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

UITableViewにクラスを登録します(たとえば、viewDidLoadに)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"];
}

UITableViewDelegateで:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"];

    // init your custom cell
    ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell;
    if (!cell) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"];
        view.cell = cell;
    }

    // Do something with your cell

    return view;
}

2

ヘッダー/フッタービューを遅延して作成するために、私は以下を使用していました:

  • セクションヘッダー/フッターのフリーフォームビューコントローラーをストーリーボードに追加します。
  • ビューコントローラーでヘッダーのすべてのものを処理します
  • テーブルビューコントローラーで、セクションヘッダー/フッターを再設定したビューコントローラーの可変配列を提供します。 [NSNull null]
  • viewForHeaderInSection / viewForFooterInSectionで、ビューコントローラーがまだ存在しない場合は、ストーリーボードinstantiateViewControllerWithIdentifierを使用して作成し、配列に記憶してビューコントローラービューを返します。

2

ヘッダーが適切な手順をすべて実行しても再利用されなかったというシナリオで問題が発生しました。

したがって、空のセクション(0行)を表示する状況を実現したいすべての人へのヒントとして、次のことに注意してください。

dequeueReusableHeaderFooterViewWithIdentifierは、少なくとも1つの行を返すまでヘッダーを再利用しません

それが役に立てば幸い


1

デイモンの提案をフォローアップするために、ここでは、開示インジケーターのある通常の行と同じようにヘッダーを選択可能にしています。

UIButtonからサブクラス化されたButton(サブクラス名 "ButtonWithArgument")をヘッダーのプロトタイプセルに追加し、タイトルテキストを削除しました(太字の "Title"テキストはプロトタイプセルの別のUILabelです)

インターフェースビルダーのボタン

次に、ボタンをヘッダービュー全体に設定し、Avarioのトリック使用して開示インジケーターを追加しました

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *CellIdentifier = @"PersonGroupHeader";
    UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(headerView == nil)
    {
        [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]];
    }

    //  https://stackoverflow.com/a/24044628/3075839
    while(headerView.contentView.gestureRecognizers.count)
    {
        [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]];
    }


    ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4];
    button.frame = headerView.bounds; // set tap area to entire header view
    button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass
    [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside];

    // https://stackoverflow.com/a/20821178/3075839
    UITableViewCell *disclosure = [[UITableViewCell alloc] init];
    disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    disclosure.userInteractionEnabled = NO;
    disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px
          (button.bounds.size.height - 20) / 2,
          20,
          20);
    [button addSubview:disclosure];

    // configure header title text

    return headerView.contentView;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 35.0f;
}

-(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer;
{
    NSLog(@"header tap");
    NSInteger section = ((NSNumber *)sender.argument).integerValue;
    // do something here
}

ButtonWithArgument.h

#import <UIKit/UIKit.h>

@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end

ButtonWithArgument.m

#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end

1

Tiemeのソリューションをベースとして使用する必要がありますがviewWithTag:、その他の魚のようなアプローチを忘れて、代わりに(そのセクションをリロードして)ヘッダーをリロードしてみてください。

したがって、カスタムセルヘッダービューにすべてAutoLayoutの装飾を施した後、デキューして、設定後にcontentViewを返すだけです。

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
 static NSString *CellIdentifier = @"SectionHeader"; 

    SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    sectionHeaderCell.myPrettyLabel.text = @"Greetings";
    sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent

    return sectionHeaderCell.contentView;
}  

1

ヘッダーがビュー配列に基づいているソリューションについてはどうですか?

class myViewController: UIViewController {
    var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in
        let headerView = UILabel()
            headerView.text = thisTitle
    return(headerView)
}

次にデリゲートで:

extension myViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return(header[section])
    }
}

1
  1. にセルを追加StoryBoardして設定reuseidentified

    sb

  2. コード

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    そして

    リンク

  3. 使用する:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }
    

2
これはヘッダーを追加する適切な方法ではありません
Manish Malviya '25

2
それではどうですか?
Pramod Shukla

1

laszlo回答に似ていますが、同じプロトタイプセルをテーブルセルとセクションヘッダーセルの両方に再利用できます。以下の最初の2つの関数をUIViewControllerサブクラスに追加します

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}

0

これがSwiftでの@Vitaliy Gozhenkoの答えです。
要約すると、UITableViewCellを含むUITableViewHeaderFooterViewを作成します。このUITableViewCellは「デキュー可能」になり、ストーリーボードで設計できます。

  1. UITableViewHeaderFooterViewクラスを作成する

    class CustomHeaderFooterView: UITableViewHeaderFooterView {
    var cell : UITableViewCell? {
        willSet {
            cell?.removeFromSuperview()
        }
        didSet {
            if let cell = cell {
                cell.frame = self.bounds
                cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth]
                self.contentView.backgroundColor = UIColor .clearColor()
                self.contentView .addSubview(cell)
            }
        }
    }
    
  2. tableDidLoad関数でこのクラスをテーブルビューにプラグインします。

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. セクションヘッダーの場合は、CustomHeaderFooterViewをデキューしてセルを挿入するように要求します

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView
        if view.cell == nil {
            let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell")
            view.cell = cell;
        }
    
        // Fill the cell with data here
    
        return view;
    }
    
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.