[iOS 8] UITableViewController の Static Cells でも動的にセルとセクション数を変更する方法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Static Cells について学ぶ

こんにちは!C87に行きたいけど、寒くて諦めました。

今回は設定画面など、レイアウトが決められたテーブルレイアウトの実装に非常に便利な UITableViewController の Static Cells についての小ネタを書きます。

先日、この StaticCells で実装をしていて途中からセルを動的に追加したいと思いましたので、その方法をご紹介します。

そもそもStatic CellsとDynamic Prototypesの違いって何?

Dynamic Prototypes

Dynamic Prototypes はセルやセクションの要素数が可変の時に用いられます。検索したデータを表示するなど、要素数が刻々と変化するものは全て Dynamic Prototypes で実装することとなります。

UITableView の前提として、スクロール操作などで画面表示領域外へ移ったセルは次々と再利用されています。少ないメモリ使用量でiOSアプリのなめらかなスクロールを実現しています。実装には一手間かかりますが、サクサク感を表現するには Dynamic Prototypes のセルの再利用は必須でしょう。

Dynamic Prototypes では UITableViewDataSource プロトコルの2メソッドを実装しなくてはならず、レイアウトが変わらないビューを作成するには、少々面倒に思えるかもしれません。通常の UITableView(Controller ではない)は全てこちらです。

作るアプリにもよりますが、使用頻度は非常に高いです。

Static Cells

Static Cells は動的に要素数を変更できない制約がある分、手軽にレイアウトが決められます。テーブルの要素についてを返すデリゲートメソッドを実装せずともテーブルレイアウトが作成できます。

主に設定画面など、アプリのバージョンアップでしか項目が変わらないようなビューの作成に適しています。

作るアプリにもよりますが、使用頻度は低いです。

Static Cellsでも動的にセルやセクション数を変える方法

Static Cells で UITableViewDataSource のデリゲートメソッドを実装すればセル数を可変にできるかと思いきやそうではなかったので、以下に原因と対処方法を記します。

Static Cells で作成された UITableView には UITableViewDataSource 以外に UITableViewDelegate プロトコルの(セルやセクション数を可変にした時に必要な)デリゲートメソッドが不足しています。

以下のデリゲートメソッドを実装することで解決できます。

  • tableView:numberOfRowsInSection:
  • tableView:cellForRowAtIndexPath:
  • tableView:heightForRowAtIndexPath:
  • tableView:canEditRowAtIndexPath:
  • tableView:canMoveRowAtIndexPath:
  • tableView:editingStyleForRowAtIndexPath:
  • tableView:indentationLevelForRowAtIndexPath:

上の3つは普通のUITableViewでしたら大体実装されているので、説明は省略します。セルの削除などを特に実装しない場合は以下のように実装すると良いでしょう。

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleNone;
}

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 高さが他のセクションと違うセルはここで指定する必要があります。
    // tableView:heightForRowAtIndexPath: を実装するだけでは可変セル・セクションの高さが変わりません。
    // tableView:heightForRowAtIndexPath: で実際の高さを返却する必要があります。
    if (indexPath.section == 1) {
        return [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:section]];
    } else {
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
}

これで Static Cells でもセルやセクションを動的に変更する事が可能となりました。

また、動的に変更する必要のないセルを返す場合には以下のようにすると簡単に行えます。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 1) {
        // 動的にセルの内容を変更する必要のある場合はこちらに記述
    } else {
        // Storyboard で設定した UI をそのまま表示したい場合はこちら
        return [super tableView:tableView cellForRowAtIndexPath:indexPath];
    }
}

以上で、 UITableViewController の StaticCells でも動的にセルやセクション数を変更できるようになります。上手に用いることで、Storyboard上である程度決まったセルのレイアウトを実装することが可能となります。

まとめ

可変するセルやセクションが多い場合は Dynamic Prototypes を使いますが、Static Cells で実装していた画面の一部要素を可変にしたいという要望はあるかと思われます。そういった状況下で使えるテクニックですので、知っておくと良いでしょう。

参考

Adding unknown number of rows to 'Static Cells' UITableView