【Swift】カスタムなUITableViewの高さを固定→可変にしようとして試行錯誤した話
共通処理部でUITableViewの高さを設定すると、個別に高さ調整したときに困る話
気付くまでに3時間ほどハマったので、こちらにメモ。
いきさつ
Dynamics Type対応に伴い、以下の構成からなる2画面(履歴、お知らせ)についてお知らせ用ViewController内にあるUITableVIewのカスタムセルの高さを可変にしようとした
画面構成
当画面は2つのViewControllerからなり、ページ切り替えで互いのページを切替可能。
- 両方ともUITableViewをもつ。
- どちらもテーブルのセルの高さは固定。
- いずれもレイアウトはObjective-C上で手打ちで設定していた。
- ファイル構成は以下の通り(ファイル名は仮名)。
- BaseViewController.mと.h:両ページの共通処理を記載
- PageOneViewController.mと.h : 過去の履歴を表示するページ(履歴ページ)
- PageTwoViewController.mと.h : お知らせ情報を表示するページ(お知らせページ)
- PageOneViewCell.mと.h : 履歴ページのUITableView用のカスタムセル
- PageTwoViewCell.mと.h : お知らせページのUITableView用のカスタムセル
PageTwoViewCellのレイアウト構成は以下の通り。両端にお知らせの写真、真ん中に本文(上)と配信日(下)が入る構成。
両矢印は幅、片矢印は位置設定
やったこと
前準備:PageTwoViewCellをxibでレイアウトし、データの反映をswiftで記載
レイアウト上の変更点は以下の通り
- 本文のbottomに配信日のTopがくっつくようConstraint追加。
- 配信日のbottomがViewCellのBottomと一致するようConstraint追加。
- PageTwoViewCellの高さをxib上で固定しない
結果:セルの高さが固定されており、セルが途中で切れて表示された
1. 予測した原因1(x):UITextView(本文)にテキスト反映後、UITextViewのintrinsicContentSizeが更新されていない
デバッガで確認したところ、intrinsticContentSizeは更新されているが想定した値よりも小さくなっていることを確認。また、この結果が-1になっていない(参考1)ことからUITextViewのスクロールが正しくオフになっていることを確認 (参考2)
2. 予測した原因2(x):UITextViewをsizeThatFitsし、計算した高さをconstraint等で設定する必要がある?
まず、UITextViewに高さのconstraintを設定。そして、上記テキスト反映直後にsizeThatFitsし、得たCGSizeの高さをcostraintに反映。
当然ながらUITextViewの高さのconstraintとUILabel(配信日)のbottomのconstraintが干渉してしまい、結果として前者が自動削除される。無意味のため元の状態に戻した。
3. 調査(x):UILabelのbottomのconstraintを一旦削除してどうなるか見てみた
以下の様に、UITextViewの表示高さは広がったものの全ては表示されず、UILabelも表示されなくなった
4. 1.-3.で詰まったので、PageTwoViewController.mでtableView:heightForRowAtIndexPath: でcellの高さをとりあえず200pxに指定(△)
UITextViewに反映したテキストは全て表示された。ただし、constraintによりその高さは200 - UILabelの高さになってしまう。
5. 問題を簡単化するため、UITextView以外を削除して表示してみる(x)
やはり上図と同様に、セルの高さが固定されていた
6. セルの高さはどこで指定していたか?と思った
そこで、BaseViewController.mを見た
真因:共通のViewControllerでセルの高さを指定していた
そもそもBaseViewController.mにて、両ページのカスタムセルの高さを指定していた。これにより、いくらPageViewController.m側でセルの高さを調整しようとしても以下の処理が優先されていた。
そこで、以下の記述をPageOneViewController.mに移した所、PageTwoViewCellController.mでテーブルのセルが正しく可変高さで表示された。
BaseViewController.m
1
2
3
4
|
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 71;
}
|
これを踏まえてやったことを振り返ってみると、以下の要員で高さを可変にできていなかった。
- 最初の結果, 1, 2:セルの高さが71pxで固定されているので、UITextViewの高さは71px - UILabelの高さ( - 余白)になってしまう。これによって、UITextViewが途中までしか表示されなかった。
- 3:UITextViewの高さはテキストの高さに合わせて設定されていた(intrinsicContentSizeの高さも正しく表示されている時の高さと一致)が、UITextViewの高さ > 71pxになっていたのでUITextViewは途中までしか表示されなかった。さらに、このUITextViewの下にUILabelが設置されていたので、UILabelはセル外に配置され表示されなかった。
反省
あらためて、設計・実装を理解しよう
他のメンバーの書いたコード(今回の箇所)について、コード変更前に関係箇所をメンバーに確認し設計・実装を理解すること。今回のように、いきなり子の部分の実装を変更しようとするのはNGです…。
継承関係のあるコードも見よう
今回のように継承関係のあるViewやViewControllerについて、親側のコードもコード変更にともなう影響を見ておく必要がある。
気分転換や切り替えは大事
4.で詰まったので別のことをして4時間ほど時間を開けて気分転換を行った。その結果、再度取り組んだときに5.および6.の内容にいきつき問題解決につながった