2013年7月16日火曜日

UITableViewについて - UITableViewのセルをカスタムセルを使ったほうがいい(1)

今回はUITableViewクラスについて疑問に思うことを書いてみようと思います。

主な疑問

UITableViewクラスを使う際に毎回、カスタムセルを使ったほうがいいのかなとずっと悩んでいました。

結論

カスタムセルについて色々しらべて以下の記事にたどり着きました。

UITableViewについて | ios-practice/docs/tableview.rst - GitHub

先に結論を書いてしまうのですが、

カスタムセルはなるべく使うようにする

ということ。

なぜかというと

上の参考記事からその理由を抜粋すると(少し長いですが)、

最初の表示更新メソッドが利用しにくくなることや dequeueReusableCellWithIdentifier: によるセルのキャッシュも利用しくくなるため、 UITableViewCellのサブクラス(いわゆるカスタムセル)を作り、Cellの拡張したViewを作る方がよい。

Controller上で [cell.contentView addSubview:] した場合に起きる問題点としては、 そのcellが dequeueReusableCellWithIdentifier: により再利用され、再びaddSubview:が行われ、 cellに対して複数回Viewが追加されてしまう事が起こってしまう。

そのため、以下のような目に見える問題やメモリ効率的にもあまり良くない場合が多い。


この問題を解決するには、以下のような方法がある。

  1. キャッシュをしない(or identifierをCell毎に変える)
  2. addSubViewする前に、CellのsubViewを除去する
  3. CellのtextLabelやaccessoryView等のデフォルトのセルコンテンツで表示する
  4. カスタムセルを作って利用する

1と2は 上記のリンクのような方法であるが、 3と4のような手法を使い表示したほうが、コード的にも綺麗に書くことができ、バグも減ると思われる。

参考記事:UITableViewについて | ios-practice/docs/tableview.rst - GitHub

と書いてあります。

つまり

セルの表示する要素が少ない場合(例えば、アイコン、タイトル、サブタイトルだけとか)は3の方法

それよりも表示する要素が多くなる場合や表示位置が複雑な物になる場合は4の方法

をとるといいということになります。

ということで

毎回カスタムセルを使ってUITableViewを生成していきます。

次回のエントリーでその作成の流れを書いてみます。

おわり

iPhoneアプリと言えば「UITableView」クラスと言われる(たぶん誰かが言ってました)くらい大切なクラスですので、しっかりとまとめていきたいです。

おまけ

カスタムセルに関して調べていて、それに関連するTipsを紹介します。

iOS6でセルの再利用方法が変わりました

UITableViewのセルの再利用がiOS6では書き方が変わっていました。

コードはこちらから抜粋します。

iOS5.1以前
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
     
    // Configure the cell...
     
    return cell;
}
iOS6以降
- (void)viewDidLoad
{
    [super viewDidLoad];
     
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
     
    // Configure the cell...
     
    return cell;
}

セルの再利用に使用するdequeueReusableCellWithIdentifier:メソッド、それによく似たdequeueReusableCellWithIdentifier:forIndexPath:メソッドがiOS SDK 6.0から新たに追加されました。

また、viewDidLoadメソッドでregisterClass:forCellReuseIdentifier:メソッドを実行し、セルの登録を行っています。

iOS6 UITableViewのセルの再利用の方法が変わった - Developers.IO

dequeueReusableCellWithIdentifierメソッドについて

上で紹介したdequeueReusableCellWithIdentifierメソッドについての説明です。

UITabelView contentView をいじるとスクロールや画面遷移でセルが重なる - gggatelier

dequeueReusableCellWithIdentifierはどうも
画面に必要な数だけUITableViewCellのインスタンスを生成
して、あとは
再描画時にそれを使い回す
という実装のようです。(その名の通りですが)

dequeueReusableCellWithIdentifierで再利用する際の注意点

セルを生成するときに付ける「Identifier(識別ID)」に関して。

dequeueReusableCellWithIdentifierで再利用する場合の罠 - Team Dyquem! ゲームプログラマが語る、自作iPhoneアプリの庭

単純なCellによるテーブルなら何も考えなくても良いのですが、デフォルトCellとカスタムCellを混在させたTable等では留意しなければいけません。
よく見かけるのは、それぞれ違う種類のCellを同じIdentifier文字列で生成しているとかでしょうか。これだとOSからは見分けが付きませんから、addSubviewされたカスタムCellなんかをデフォルトCellとして再利用指示がきたりします。当然画面がぶっ壊れますのでスグに気がつきますけれど。

基本は用途毎にIdentifierで紐付け。(文字通り"identify"です )
Cellに関する初期化系コードの全ては生成時、つまりdequeueReusableCellWithIdentifierがnilを返した時のみに行います。その上で、通常は要素の更新だけを行う形にするのが正解です。(addしたsubviewを弄る等)

Identifierは用途毎に付け、付ける際はdequeueReusableCellWithIdentifier:メソッドがnilを返したときに行う。

カスタマイズされたUITableViewCellを使う


カスタマイズされたUITableViewCellを使う - @blog.justoneplanet.info

カスタマイズセルを作成するのに参考になりそうな記事。

端末が回転終了したときにセルを再設定する


tableのcellの再設定 - Bugle Diary

iPhoneなど端末を回転させてビューが縦もしくは横画面に表示が切り替わったときにセルの再設定を行うちょっとマニアックな記事。

UITableViewについての基本が学べる記事

UITableViewについて手っ取り早く理解を深めたいのであればとりあえず以下の記事がおすすめです。


1つ目はおなじみ iPhoneアプリ開発の虎の巻さんのページ。残りはMixiさんが書かれているiOSアプリトレーニングコースの内容のものです。こちらはARC前提で書かれているようです。

0 件のコメント:

コメントを投稿