【Swift】TableViewCell の中に CollectionView を配置したときの TableViewCell の高さについて

iOS, Swift

この記事は最終更新日から1年以上が経過しています。

この記事を書くに至った経緯

TableViewCell の中に、CollectionViewを配置する手段を用いて開発をしていました。
その中で、CollectionViewは2列で表示されているのに、TableViewCellの長さはCollectionView1列分の長さになっているという事象が発生しました。
この問題の解決に2日要したので記事にしようと思いました。

再現gif

longTableViewCell.gif

作業環境

  • macOS Catalina
    • version 10.15.5
  • Xcode
    • version 12.3
  • iPhone12 Pro Max (シミュレーター)
    • iOS 14.3
  • Swift
    • version 5.3.2

TOC(Table Of Contents)

  • 発生した問題
  • 解決策
  • 高さが揃わない事象
    • TableViewCellの画面更新を行わない場合
    • 解決策コードを記述しなかった場合
  • layoutIfNeeded()の動き
  • まとめ
  • 最後に
  • 参考サイト一覧

発生した問題

TableViewCellの高さが決まるタイミングと、CollectionViewのセルの高さが決まるタイミングにズレがあるのか、期待通りの高さになりませんでした。

なんとなく、TableViewCellをlayoutIfNeeded()で画面更新すれば解決するのではないかと思っていましたが、そのタイミングが分からず苦戦しました。

原因については、現時点では推測です。解決策とともに、原因についても本記事でご紹介します。

解決策

TableViewCellのクラス内で、以下の記述を行うことで解決することができました。

self.layoutIfNeeded() を行うことで、TableViewCell自身の画面更新と、TableView配下のView(CollectionViewとその中)のレイアウト更新を行い、情報を新しいものに更新することができます。

以下の systemLayoutSizeFitting() というメソッドは、TableViewCellが作成された段階で読み込まれます。つまりこの時点で、親Viewに対して layoutIfNeeded() を行っておけば、レイアウト更新のフラグが立った時点で更新が行われる為、レイアウト崩れを起こさなくなります。

コード

gif

adjustedTableViewCell.gif

各メソッド内でprint()をして動きを見る

レイアウト更新が走るタイミングと、その他説明をコメント文で記載しました。

高さが揃わない事象

TableViewCellの画面更新を行わない場合

コード

gif

longTableViewCell.gif

各メソッド内でprint()をして動きを見る

レイアウト更新が走るタイミングと、その他説明をコメント文で記載しました。

解決策コードを記述しなかった場合

スクリーンショット

Simulator Screen Shot - iPhone 12 Pro Max - 2020-12-22 at 15.16.09.png

各メソッド内でprint()をして動きを見る

レイアウト更新が走るタイミングと、その他説明をコメント文で記載しました。

layoutIfNeeded()の動き

systemLayoutSizeFitting() をoverride して layoutIfNeeded() をしている事象を見てみると、自分自身(ここではtableViewCell)と配下のViewの更新が全て終わるまで、次の動きを待機していることがわかります。
調べてみると、あるサイトに、

この処理を呼んだ場合再描画処理が同期実行されます。その為、再描画が完了するまで呼び出し側の実行が止まります。

と記載してありました。

また、layoutIfNeeded()を行うと、親View → 子View の順番でレイアウトの更新が走ります。

参考サイト

まとめ

根本原因

いろいろな場所で print() をした結果、今回の原因は、タイミングではありませんでした

  • CollectionView の width を TableViewCell の width に合わせたか
  • CollectionViewCell二列分の幅が、サイズ計算が始まる前に担保されていたか

上記の2点だったように思います。

今回のコードでは、以下のように、CollectionViewCell の width を TableViewCell の width の約半分として設定しています。

故に、CollectionView の width が、TableViewCell の width より狭かった場合、CollectionViewCell が二列収まらなくなり、一列分として計算を始めてしまいます。

解決策(再掲)

TableViewCell の中に、CollectionView を入れるときは、

  • systemLayoutSizeFitting()override
  • その中で TableViewCell の layoutIfNeeded() を実行

CollectionView の size を TableViewCell の size に更新してくれます。

最後に

これから、UITableViewが使われなくなっていくと聞いていますが、もし CollectionView in TableViewCell という複雑なやり方をするのであれば、参考にしていただければと思います。

最後まで記事を読んで頂き、ありがとうございました。

参考サイト一覧

iOS, Swift