2011年12月18日日曜日

Storyboardでテーブルビューのセルをカスタマイズする (iOS)

StoryboardではTableViewのセルのカスタマイズも1画面でできるようになりました。

セルのプロトタイプから動的にセルを生成する

カスタムセルのプロトタイプを作って、プログラムで動的にセルの内容を決定するTableViewを作ってみます。

最初に、プロジェクトを作るところから始めます。Master Detail Applicationテンプレートから新規プロジェクトを作ります。

Storyboardを開くと、すでにテーブルビューとセルがあります。このセルをカスタマイズします。

TableViewのAttribute inspectorでContent属性をDynamic Prototypesに変更します。ここをStatic Cellsにするとセルのインスタンス1つ1つをInterface Builderで作ることになります。今回はデータを動的に埋め込むのでDynamic Prototypesにします。

セルのAttribute inspectorでStyle属性をCustomにします。Identifierは”CustomCell”と名づけました。このカスタムセルにラベルを2つ乗せて、左のラベルのタグを1に、右のラベルのタグを2にします。

セルをタップしたらDetail Viewに遷移するようにします。

セルから遷移先のビュー(Detail View)にCtrlキーを押しながら名マウスをドラッグしてSegueを作成します。こうするとコードを書かなくてもセルからの画面遷移が作成できます。この場合、どのセルも同じ画面への遷移になります。

各セルの表示内容を決定するためのコードを書きます。cellForRowAtIndexPath:メソッドでセルの値を設定します。

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

-(NSInteger)tableView:(UITableView *)tableView
        numberOfRowsInSection:(NSInteger)section {
    return 3;
}

-(UITableViewCell *)tableView:(UITableView *)tableView
        cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   
    UITableViewCell *cell = [tableView
        dequeueReusableCellWithIdentifier:@"CustomCell"];
    UILabel *label1 = (UILabel*)[cell viewWithTag:1];
    UILabel *label2 = (UILabel*)[cell viewWithTag:2];
   
    label1.text = [[NSString alloc] initWithFormat:@"Section %d", indexPath.section];
    label2.text = [[NSString alloc] initWithFormat:@"Row %d", indexPath.row];

    return cell;
   
}

以前はUITableViewのdequeueReusableCellWithIdentifier:でセルのインスタンスがなければ新規にallocするコードを書きましたが、Storyboardでセルのプロトタイプを作ると最初からインスタンスがあるようなので、再利用可能なセルがなかった場合のコードは省くことができます。

複数のセルプロトタイプを使い分ける

Object libraryの中からTable View CellをTable Viewにドラッグしてセルのプロトタイプを新しく追加します。

セルのIdentifierを”CustomCell2”にします。

最初のプロトタイプと同様、ラベルを2つ追加します。少しレイアウトを変えて縦に2つ並べてみます。上のラベルから1, 2とタグを付けます。

いま作成したセルのプロトタイプからDetail ViewにSegueを作成します。

セクションを2つにして、セクションのヘッダを付けます。

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 2;
}

-(NSInteger)tableView:(UITableView *)tableView
        numberOfRowsInSection:(NSInteger)section {
    return 3;
}

- (NSString *)tableView:(UITableView *)tableView
        titleForHeaderInSection:(NSInteger)section {
   
    return [[NSString alloc] initWithFormat:@"Section %d", section];
   
}

cellForRowAtIndexPath: をセクションごとに別のプロトタイプを使うように変更します。ラベルの数とタグはどちらも同じなので同じコードを利用します。

-(UITableViewCell *)tableView:(UITableView *)tableView
        cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   
    UITableViewCell *cell;
   
    if ( indexPath.section == 0 ) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
    }
    else {
        cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell2"];
    }
    UILabel *label1 = (UILabel*)[cell viewWithTag:1];
    UILabel *label2 = (UILabel*)[cell viewWithTag:2];
   
    label1.text = [[NSString alloc] initWithFormat:@"Section %d", indexPath.section];
    label2.text = [[NSString alloc] initWithFormat:@"Row %d", indexPath.row];

    return cell;
   
}

実行すると、セクションごとに別のレイアウトのセルが表示されます。セルをタップするとどのセルも同じ画面に遷移します。

セルごとに別の画面に遷移する

上記の方法ではどのセルも同じ画面に遷移していました。しかし、セルごとに別々の画面に遷移したいこともあると思います。セルからの画面遷移を動的に決定するプログラムを作成してみます。

先ほど作ったセルのSegueをすべて削除します。代わりにView Controllerから遷移先の画面にSegueを1つ作成します。

TableViewのdelegete(大抵の場合、この画面のView Controller)にdidSelectRowAtIndexPath:メソッドを実装します。ここでperformSegueWithIdentifierを呼び出して画面遷移をします。下記の例では常に同じSegueを実行していますが、indexPathの値に応じて引数のIdentifierを決定すればセルごとに任意の画面に遷移できます。

-(void)tableView:(UITableView *)tableView
        didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
   
    [self performSegueWithIdentifier:@"detail" sender:self];
   
}