文系プログラマの勉強ノート

スマホアプリ開発やデザインなどについて勉強したことをまとめています

【Xcode】文字数に応じてUITableViewの行の高さを変える

お買い物計画アプリの買い物リスト部分が完成しました。

f:id:an3714106:20140117204417p:plain

アプリを起動すると左のリスト一覧画面が表示されます。
ここでリストを作ってタップすると右の各リスト画面に遷移して、
具体的なほしいもの、予算、個数、メモや購入済みチェックボタンなどを入力できます。
いたって普通の買い物リストですね。

リスト一覧画面はUICollectionView、各リスト画面はUITableViewを使いました。

ところで前回と比べると、セル内の文字数に応じてUITableViewの行の高さが変わっていると思います。
色々やり方があるようですが、今回使った方法を一例としてご紹介。

文字数に応じてUITableViewの行の高さを変える

カスタムセルの構成

今回カスタムセルを使っていまして、UIButton(黄色)が1つとUILabelが3つという構成です。
ラベルは上から項目名(青)、予算×個数(緑)、メモ(オレンジ)となっていて、
メモは空欄の場合も複数行にわたる場合もあります。
つまりUITableViewの行の高さを変えるとき考慮すべきは、メモの高さということになります。

f:id:an3714106:20140117211543p:plain

UITableViewの行の高さを指定する

UITableViewの行の高さは「tableView: heightForRowAtIndexPath:」で指定します。
_itemListはNSArrayで、メモに表示する文字列が格納されているものとします。
仮のメモ用ラベルを作成して高さを求め、その他のラベルの高さや余白などを計算して返します。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 表示文字列を取得
NSString* memo = _itemList[indexPath.row];

// サイズを計算
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 258, 0)]; // ラベルの横幅は固定
label.numberOfLines = 0;
label.font = [UIFont systemFontOfSize:13.0f];
label.text = memo;
[label sizeToFit];

// 上の余白+高さ(+下の余白)
float nameLabelHeight = 5.f + 21.f;
float budgetLabelHeight = 2.f + 20.f;
float memoLabelHeight = 6.f + label.frame.size.height + 5.f;
return ( nameLabelHeight + budgetLabelHeight + memoLabelHeight);
}

UITableViewCellの中身のサイズ・位置調整

次にUITableViewCellの中身のサイズ・位置を調整します。
カスタムセルのクラスに以下のメソッドを追加します。
高さ可変のメモ用ラベルのサイズを調整し、
チェックボタンがセルの垂直方向の中央に来るように位置を調整します。

- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];

// memoLabelのサイズを計算
[self.memoLabel sizeToFit];

// checkmarkの位置を計算
self.checkButton.frame = CGRectMake(self.checkButton.frame.origin.x,
(self.frame.size.height - self.checkButton.frame.size.height) / 2,
self.checkButton.frame.size.width,
self.checkButton.frame.size.height);
}

なぜか戻ってしまうCellの中身のサイズ・位置を再調整

上記で文字数に応じてUITableViewの行の高さが変わるようになったと思います。

しかし、UITableViewをスクロールしたときなど、再描画されると
なぜかCellの中身のレイアウトが初期状態に戻ってしまうことがあります。
仕方ないので、UITableViewを所有するUIViewControllerクラスに
以下の様なメソッドを追加し、適宜呼ぶことにしました。
一瞬初期状態のレイアウトが表示されてしまうのでいまいちな解決策ですが、
今回はこのくらいで勘弁してやるか、ということにしました。
2015/1/11追記
Cellの中身のレイアウトが初期状態に戻るのは、AutoLayoutがONになっていたからでした。

- (void)relayoutTableView
{
// TableViewの全行のセルを取り出し、レイアウト調整処理を呼ぶ
for ( int i = 0; i <[_tableView numberOfRowsInSection:0]; i++ ) {
CustomCell* cell = (ItemCell*)[_tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
[cell drawRect:cell.frame];
}
}


......................................

UICollectionViewも同様の方法でセルの高さを変えることが可能です。
ただUICollectionViewの場合、高さを変えても自動でセル間の余白を詰めてくれません。
それをやるにはカスタムレイアウトを作成しないといけないとか、なんとか。
リスト一覧としては固定サイズの方が見やすそうなので、今回は勘弁してやるか、ということにしました。



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2015/1/11追記
iOS8からはtableViewのrowHeightプロパティにUITableViewAutomaticDimensionを設定することで
セルの高さを自動計算してくれるようになりました。
下記のサイト様が参考になります。

ついにTableViewCellの高さが自動で調整してくれるようになった件!!! - Qiita

UITableViewCellの高さを自動で計算する: UITableViewAutomaticDimension - tomoyaonishiのブログ
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *