1. ホーム
  2. アイオス

[解決済み】UICollectionView 自動レイアウトでセルサイズを変更する方法

2022-04-06 03:33:31

質問

セルフサイジングに挑戦しています UICollectionViewCells Auto Layout で動作していますが、セルのサイズをコンテンツに合わせられないようです。セルのサイズが、セルのcontentViewの中の内容からどのように更新されるのか理解するのに苦労しています。

私が試した設定は以下の通りです。

  • カスタム UICollectionViewCell を持つ UITextView をそのcontentViewに追加します。
  • をスクロールさせる。 UITextView は無効です。
  • contentViewの水平方向の制約は、"H:|[_textView(320)]"であり、つまりは UITextView は、明示的に幅が320のセルの左側に固定されます。
  • contentViewの垂直方向の制約は、"V:|-0-[_textView]"であり、すなわち UITextView セルの上部に固定されます。
  • は、その UITextView は高さの制約を定数に設定し、それを UITextView がテキストにフィットすることを報告します。

セルの背景を赤に設定し、さらに UITextView の背景をBlueに設定します。

今まで遊んでいたプロジェクトをGitHubに置いてみました こちら .

解決方法は?

<ブロッククオート

この回答は、コンポジションレイアウトが追加されたiOS 14から古くなったものです。を更新することをご検討ください。 新規API

Swift 5に対応したアップデート

preferredLayoutAttributesFittingAttributes に改名されました。 preferredLayoutAttributesFitting で、オートサイジングを使用する


Swift 4に対応したアップデート

systemLayoutSizeFittingSize に改名されました。 systemLayoutSizeFitting


iOS 9に対応したアップデート

私のGitHubのソリューションがiOS 9で壊れるのを見た後、私はようやくこの問題を完全に調査する時間を得ました。現在、レポを更新し、セルフサイジングセルのさまざまな設定の例をいくつか含んでいます。私の結論は、セルフサイジングセルは理論的には素晴らしいが、実際には厄介であるということです。セルフサイジングセルを使用する際には注意が必要です。

TL;DR

私のチェックアウト GitHubプロジェクト


セルフサイズセルはフローレイアウトでのみサポートされています。

セルフサイジングセルを動作させるには、2つの設定を行う必要があります。

#1. 設定 estimatedItemSize オン UICollectionViewFlowLayout

を設定すると、フローレイアウトがダイナミックになります。 estimatedItemSize プロパティを使用します。

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

#2. セルのサブクラスでサイジングのサポートを追加する

これには2つの種類があります; 自動レイアウト または のカスタムオーバーライドです。 preferredLayoutAttributesFittingAttributes .

オートレイアウトでセルを作成・設定する

この点に関しては、優れた SOの記事 セルに対する制約の設定について。ただ、注意しなければならないのは Xcode 6 が壊れた iOS 7をサポートする場合、セルのcontentViewにautoresizingMaskを設定し、セルが読み込まれたときにcontentViewの境界がセルの境界として設定されるようにする必要があります(例. awakeFromNib ).

注意しなければならないのは、セルはテーブルビューセルよりも深刻な制約を受ける必要があることです。例えば、幅をダイナミックにしたい場合、セルには高さの制約が必要です。同様に、高さを動的にしたい場合は、セルに幅の制約が必要です。

実装 preferredLayoutAttributesFittingAttributes をカスタムセルで使用します。

この関数が呼ばれたとき、ビューにはすでにコンテンツが設定されています (例. cellForItem が呼び出されました)。制約が適切に設定されていると仮定すると、次のような実装が可能です。

//forces the system to do one layout pass
var isHeightCalculated: Bool = false

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    //Exhibit A - We need to cache our calculation to prevent a crash.
    if !isHeightCalculated {
        setNeedsLayout()
        layoutIfNeeded()
        let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
        var newFrame = layoutAttributes.frame
        newFrame.size.width = CGFloat(ceilf(Float(size.width)))
        layoutAttributes.frame = newFrame
        isHeightCalculated = true
    }
    return layoutAttributes
}

ノート iOS 9では動作が少し変わり、気をつけないと実装がクラッシュする可能性があります(詳細はこちら こちら ). 実装する場合 preferredLayoutAttributesFittingAttributes の場合、レイアウト属性のフレームを一度だけ変更することを保証する必要があります。これを行わないと、レイアウトはあなたの実装を無限に呼び出し、最終的にはクラッシュします。一つの解決策は、計算されたサイズをセル内にキャッシュし、セルを再利用したり、その内容を変更したりするときはいつでもこれを無効にすることで、これは私が isHeightCalculated プロパティを指定します。

レイアウトを体験する

この時点で、collectionViewに「機能する」ダイナミックセルがあるはずです。私はテスト中に、すぐに使える解決策をまだ見つけていないので、もし見つけた場合は遠慮なくコメントしてください。まだ UITableView は、ダイナミックサイジングの戦いに勝つと私は思います。

#キャバ嬢 プロトタイプセルを使用して推定アイテムサイズを計算する場合、以下のような問題が発生することに注意してください。 XIB はサイズクラスを使用します。 . この理由は、XIB からセルを読み込むと、そのサイズクラスが次のように設定されるからです。 Undefined . iOS 7 では、サイズクラスはデバイスに基づいて読み込まれるため(iPad = Regular-Any、iPhone = Compact-Any)、これは iOS 8 以降でのみ壊れるでしょう。XIBをロードせずにestimatedItemSizeを設定するか、XIBからセルをロードしてcollectionViewに追加し(これはtraitCollectionを設定します)、レイアウトを実行して、スーパービューからそれを削除することができます。また、セルに traitCollection を取得し、適切な形質を返します。それはあなた次第です。

もし何か見逃したものがあれば教えてください。お役に立てれば幸いです。