基本的にはこれから作るアプリはiOS7.0以上を対応OSとしたいと考えているので、Auto Layout を用いた形で、できるだけ簡単・合理的にビューのサイズを制御したい。
今回はカスタムセル上にUILabel
を載せ、この高さが可変なのでセルの高さも変わる、というケースを想定している。
コードとしては、ほぼこちらのページの流用です。
[iOS] Auto Layout + Storyboard で高さ可変のUITableViewCellを作成する
How to resize superview to fit all subviews with autolayout?
なお、結論から言うと、やりたいことの確認はできました。しかし、
ステップ4の部分でどうしても疑問が解消しないコトがあった。
もし似たような疑問をお持ちで、解決した方がいらっしゃいましたらヒントもらえるとうれしいですm(_ _)m。
1. カスタムセルのxibを作成し、AutoLayoutを適用する
UILabel
を適当に配置した状態で、Resolive Auto Layout Issue
-> Add Missing Constraints in カスタムセルクラス名
を実行して制約を作る。
2. カスタムセルの.m/.hファイルを作成する
xibファイル側のパーツと接続するアウトレットを宣言しておく。
3. 計算用のセルをプロパティに用意する
計算用のセルは表示に使うことはなく、高さ計算の度に利用される。
- (void)viewDidLoad
{
[super viewDidLoad];
//カスタムセルの登録
[self.tableView registerNib:[UINib nibWithNibName:CellIdentifier bundle:nil] forCellReuseIdentifier:CellIdentifier];
//計算用セルの生成
_stubCell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
4.tableView:heightForRowAtIndexPath:
を実装する
ここでは計算用のセルを使い、パラメータのindexPath
に対応するモデル情報を配置した場合の高さを計算する。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//計算用セルを使って、該当情報を設定する
[self configureCell:_stubCell atIndexPath:indexPath];
//①可変要素のあるサブビューの高さを計算
CGFloat fixedWidth = _stubCell.bodyLabel.frame.size.width;
CGSize sizeThatShouldFitTheContent = [_stubCell.bodyLabel sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
CGRect newFrame = _stubCell.bodyLabel.frame;
newFrame.size = sizeThatShouldFitTheContent;
_stubCell.bodyLabel.frame = newFrame;
//②サブビューの再配置を強制
[_stubCell setNeedsLayout];
[_stubCell layoutIfNeeded];
//③カスタムセルの ContentView のサイズを再計算し、高さを決定する
CGFloat height = [_stubCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height + 1;
}
疑問A
気持ち的には、①、②、③の処理を順序立てて行うことで、セルの高さ計算が完結するように思えるが、実際のところは③だけ実装すれば、期待する高さを返却できた。
①サブビューの高さを計算して、フレームに設定
②サブビューの形が変わったので再配置
③再配置後に、制約をふまえてセル自体の高さがどうなったか問い合わせ
というイメージがあるのだが、①、②を省略していい理由はどこかに記載があるのだろうか。
ちょっとわからなかった。
疑問B
可変対象のサブビューをUILabel
ではなくUITextView
にしたところ、①で高さが変更されたことが確認できたものの、③のタイミングではIBで設定している初期値の高さが返ってきた(つまり、セルの高さは常に一定になる)。
5.tableView:estimatedHeightForRowAtIndexPath:
を実装する
tableView:heightForRowAtIndexPath:
での再計算の処理コストは高いので、見積もり用の高さをかえしておくことで実際の高さ計算を遅延させる(iOS7からの機能)。
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 40; //40はテキトウな数字
}
6.セル表示用のメソッドtableView:cellForRowAtIndexPath:
を実装する
おなじみのデータソースメソッド。セル固有の処理はconfigureCell:atIndexPath:
にて行っている。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RMCommentListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
[self configureCell:cell atIndexPath:indexPath];
return cell;
}