UICollectionView自動レイアウトによるセルのサイズ変更セル


207

UICollectionViewCells自動レイアウトでセルフサイズ設定を機能させようとしていますが、セルをコンテンツに合わせてサイズ調整できません。セルのcontentViewの内容からセルのサイズが更新される方法を理解できません。

これが私が試した設定です:

  • contentViewにUICollectionViewCella UITextViewを含むカスタム。
  • のスクロールUITextViewは無効です。
  • contentViewの水平方向の制約は「H:| [_textView(320)]」です。つまり、これUITextViewは明示的な幅320でセルの左側に固定されています。
  • contentViewの垂直方向の制約は、「V:| -0-[_ textView]」、つまりUITextViewセルの上部に固定されています。
  • UITextViewは、UITextViewレポートがテキストに適合する定数に設定された高さ制約があります。

セルの背景を赤に、UITextView背景を青に設定すると、次のようになります。 セルの背景が赤、UITextViewの背景が青

これまでに使用してきたプロジェクトをGitHubに配置しました


sizeForItemAtIndexPathメソッドを実装していますか?
Daniel Galasko 2014

@DanielGalasko私は違います。私の理解では、iOS 8の新しいセルフサイズセル機能では、これを行う必要はありません。
rawbee 2014

その情報をどこから入手したかはわかり
ダニエルGalasko

しかし、再び、それはあなたがestimatedItemSizeを実装作る、あなたのユースケースに依存
ダニエルGalasko

回答:


309

Swift 5用に更新

preferredLayoutAttributesFittingAttributes名前が変更されpreferredLayoutAttributesFitting、自動サイズ設定が使用されます


Swift 4用に更新

systemLayoutSizeFittingSize に改名 systemLayoutSizeFitting


iOS 9向けに更新

iOS 9でGitHubソリューションが壊れたことを確認した後、ようやく問題を完全に調査する時間を得ました。セルのサイズを変更するためのさまざまな構成の例をいくつか含むように、リポジトリを更新しました。私の結論は、セルのサイズ設定は理論的には素晴らしいが実際には厄介であるということです。セルのサイズを自動調整する場合の注意点。

TL; DR

私のGitHubプロジェクトをチェックしてください


セルのサイズ変更はフローレイアウトでのみサポートされているので、使用しているものを確認してください。

セルのセルフサイジングを機能させるには、2つの設定が必要です。

1.設定estimatedItemSizeUICollectionViewFlowLayout

estimatedItemSizeプロパティを設定すると、フローレイアウトは本質的に動的になります。

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

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

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

自動レイアウトでセルを作成して構成する

セルの制約の構成に関する素晴らしいSOの投稿があるので、これについては詳しく説明しません。ちょうどその警戒するのXcode 6が壊れたので、iOSの7で原料の束をあなたがiOSの7をサポートしている場合autoresizingMaskは、セルのcontentViewに設定し、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するときは、レイアウト属性のフレームを一度だけ変更する必要があります。これを行わないと、レイアウトが実装を無期限に呼び出し、最終的にクラッシュします。1つの解決策は、計算されたサイズをセルにキャッシュし、セルを再利用したり、コンテンツを変更したりするたびにこれを無効にすることです。isHeightCalculatedプロパティで。

レイアウトを体験

この時点で、collectionViewに「機能している」動的セルがあるはずです。私のテストでは、すぐに使える十分な解決策をまだ見つけていません。よろしければコメントしてください。それはまだのように感じていますUITableView、ダイナミックなサイズ変更のIMHOの戦いに勝利したます。

注意事項

プロトタイプセルを使用して見積もられたアイテムサイズを計算している場合、XIBがサイズクラスを使用している場合、これは機能しなくなることに注意してください。これは、XIBからセルをロードすると、そのサイズクラスがで構成されるためUndefinedです。iOS 7ではサイズクラスがデバイスに基づいてロードされるため、これはiOS 8以降でのみ機能します(iPad =標準-Any、iPhone =コンパクト-Any)。XIBを読み込まずに見積もったアイテムサイズを設定するか、またはXIBからセルを読み込んで、それをcollectionViewに追加し(これによりtraitCollectionが設定されます)、レイアウトを実行してから、スーパービューから削除します。あるいは、セルにtraitCollectionゲッターをオーバーライドさせ、適切な特性を返すようにすることもできます。それはあなた次第です。

私が何かを見逃した場合はお知らせください、私が助けて幸運なコーディングを願っています



2
フローレイアウトで同じことを実装しようとしていますが、更新されたサイズでnewFrameを返すと、レイアウトはy位置を再計算していないようです。何が欠けていますか?
アンナ2014年

@annaレイアウトでinvalidateLayoutを呼び出していますか?
Daniel Galasko、2014年

1
ああ、違いがわかったので、あなたは推定高さをかなり高く(400)設定しましたが、私は最小値(44)を行っていました。それは役に立ちました。ただし、コンテンツサイズは、最後までスクロールするまで正しく設定されていないように見えるため、あまり使い道がありません。ところで、私は同じ動作のUITextViewをUILabelに変更しました。
anna 2014年

17
これはiOS 9 GMでは根本的に壊れているようです。設定estimatedItemSizeはクラッシュにつながります-自動レイアウトがUICollectionViewCellを処理しようとする方法にいくつかの大きなバグがあるようです。
Wes Campaigne

3
同様の問題が発生した場合、誰かが回避策を見つけましたか?
Tony

47

iOS10にはUICollectionViewFlowLayout.automaticSize(以前のUICollectionViewFlowLayoutAutomaticSize)という新しい定数があるので、代わりに:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

あなたはこれを使うことができます:

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

特にコレクションビューのセルに一定の幅がある場合、パフォーマンスが向上します

フローレイアウトへのアクセス:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
   }
}

Swift 5更新:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }
}

8
これはどこに設定しますか?viewdidload?どのようにフローレイアウトを処理しますか?
UKDataGeek 2017

1
私はこれをなんとか実装しましたが、単一のセルがある場合にセルを中央に配置するという奇妙な動作があります。ここではそれについてのスタックオーバーフローの問題がある-私は困惑している: stackoverflow.com/questions/43266924/...
UKDataGeek

1
collectionViews collectionViewLayoutを介してフローレイアウトにアクセスし、それがタイプであることを確認するだけですUICollectionViewFlowLayout
d4Rk

4
それは私の細胞を非常に小さく、役に立たなくします
user924

44

Daniel Galaskoの回答に対するいくつかの重要な変更により、すべての問題が修正されました。残念ながら、(まだ)直接コメントできるほどの評判はありません。

ステップ1で自動レイアウトを使用する場合は、単一の親UIViewをセルに追加するだけです。セル内のすべてが親のサブビューである必要があります。これで私の問題はすべて解決しました。XcodeはこれをUITableViewCellsに自動的に追加しますが、UICollectionViewCellsには​​追加しません(ただし、追加する必要があります)。ドキュメントによると:

セルの外観を構成するには、データアイテムのコンテンツをサブビューとして表示するために必要なビューをcontentViewプロパティのビューに追加します。セル自体に直接サブビューを追加しないでください。

次に、手順3を完全にスキップします。必要ありません。


1
面白い; これはXibs専用ですが、それでも感謝します。Githubプロジェクトをいじくり回して、再現できるかどうかを確認します。答えをXibとプログラマティックに分けるかもしれません
Daniel Galasko

非常に役立ちます 。ありがとう!
ProblemSlover 2015

2
iOS 9、Xcode7。セルはストーリーボードにプロトタイプされ、カスタムサブクラスが設定されます。と呼ばれるプロパティを作成しようとしましたがcontentView、Xcodeは既存のプロパティと競合していると警告します。サブビューを追加self.contentViewして制約を設定しようとすると、アプリがクラッシュしました。
Nicolas Miari、2015年

@NicolasMiariプログラムでこれを行う方法がわからない。私の解決策は、すべてがXIB内に入る単一のuiviewをXIBに追加することです。プロパティなどを作成する必要はありません。
Matt Koala

@NicolasMiariと同じ問題に直面しています。iOS 9 / Xcode 7でautolayout制約を使用してストーリーボードでプロトタイプ化されたセルに見積もられたコンテンツサイズを設定すると、不正アクセス例外と有用なスタックトレースなしでアプリがクラッシュする
wasabi

34

iOS 10以降では、これは非常にシンプルな2ステップのプロセスです。

  1. すべてのセルの内容が単一のUIView内に配置されていることを確認してください(またはUIStackViewのようなUIViewの子孫の内部にあり、自動レイアウトが大幅に簡素化されています)。UITableViewCellsを動的にサイズ変更する場合と同様に、ビュー階層全体で、最も外側のコンテナーから最も内側のビューまで、制約を構成する必要があります。これには、UICollectionViewCellと直接の子ビューの間の制約が含まれます

  2. UICollectionViewのフローレイアウトに自動サイズ調整を指示する

    yourFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

不思議なことに、UIViewでセルのコンテンツをラップすると、自動レイアウトのパフォーマンスがどのように向上しますか?より浅い階層でパフォーマンスが向上すると思っていましたか?
James Heald、2017

1
パフォーマンスについては言及しませんでした。セルの自動サイズ設定は、制約が左から右、および上から下に及ぶ場合にのみ機能します。これは、すべてのビューが単一のコンテナーにラップされている場合に最も簡単に実現できます。そのコンテナーがUIStackViewである場合、UIViewの他の子孫である場合よりも簡単です。
Marmoy、2017

UICollectionViewFlowLayoutAutomaticSizeの高さ定数を設定する方法を教えてください(高さ25を設定すると、幅は自動的に変更されます)。
ZhengGe Che

XcodeはSwift 4.1を使用しUICollectionViewFlowLayout.automaticSizeて、名前がに変更されたことを教えてくれますUICollectionViewFlowLayoutAutomaticSize
LinusGeffarth

14
  1. viewDidLoad()にflowLayoutを追加します

    override func viewDidLoad() {
        super.viewDidLoad() 
        if let flowLayout = infoCollection.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.estimatedItemSize = CGSize(width: 1, height:1)
        }
    }
  2. また、UIViewをセルのmainContainerとして設定し、セル内に必要なすべてのビューを追加します。

  3. 詳細については、この素晴らしい、驚くべきチュートリアルを参照してください: iOS 9および10でautolayoutを使用してセルを自動サイズ設定するUICollectionView


11

編集11/19/19:iOS 13の場合、推定の高さでUICollectionViewCompositionalLayoutを使用します。この壊れたAPIの処理に時間を無駄にしないでください。

しばらくこれに取り組んだ後、スクロールを無効にしないと、UITextViewsのサイズ変更が機能しないことに気づきました。

let textView = UITextView()
textView.scrollEnabled = false

スクロールしようとすると、コレクションビューのセルがスムーズではありません。これに対する解決策はありますか?
Sathish Kumar Gurunathan

6

contentViewアンカーの謎:

ある奇妙なケースでは

    contentView.translatesAutoresizingMaskIntoConstraints = false

動作しません。contentViewに4つの明示的なアンカーを追加し、機能しました。

class AnnoyingCell: UICollectionViewCell {
    
    @IBOutlet var word: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame); common() }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder); common() }
    
    private func common() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

そしていつものように

    estimatedItemSize = UICollectionViewFlowLayout.automaticSize

YourLayout: UICollectionViewFlowLayout

知るか?誰かを助けるかもしれません。

クレジット

https://www.vadimbulavin.com/collection-view-cells-self-sizing/

そこの先端に偶然出会った-これに関するすべての1000年代の記事のどこにもそれを見たことがない。


3

コレクションビューの動的なセルの高さを行いました。ここにgitハブのリポジトリがありますです。

そして、preferredLayoutAttributesFittingAttributesが2回以上呼び出される理由を調べます。実際には、少なくとも3回呼び出されます。

コンソールログ画像: ここに画像の説明を入力してください

1番目のpreferredLayoutAttributesFittingAttributes

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c290e0> index path: (<NSIndexPath:    0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 57.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

layoutAttributes.frame.size.heightは現在のステータス57.5です。

2番目のpreferredLayoutAttributesFittingAttributes

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa405c16370> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 0);

セルフレームの高さが予想どおり534.5に変更されました。ただし、コレクションビューはまだ高さがゼロです。

3番目のpreferredLayoutAttributesFittingAttributes

(lldb) po layoutAttributes
<UICollectionViewLayoutAttributes: 0x7fa403d516a0> index path: (<NSIndexPath: 0xc000000000000016> 
{length = 2, path = 0 - 0}); frame = (15 12; 384 534.5); 

(lldb) po self.collectionView
<UICollectionView: 0x7fa40606c800; frame = (0 57.6667; 384 477);

コレクションビューの高さが0から477に変更されたことがわかります。

動作はスクロールの処理に似ています。

1. Before self-sizing cell

2. Validated self-sizing cell again after other cells recalculated.

3. Did changed self-sizing cell

最初は、このメソッドは一度だけ呼び出すと思いました。だから私は次のようにコーディングしました:

CGRect frame = layoutAttributes.frame;
frame.size.height = frame.size.height + self.collectionView.contentSize.height;
UICollectionViewLayoutAttributes* newAttributes = [layoutAttributes copy];
newAttributes.frame = frame;
return newAttributes;

この行:

frame.size.height = frame.size.height + self.collectionView.contentSize.height;

システムコールの無限ループとアプリのクラッシュを引き起こします。

サイズが変更されると、すべてのセルの位置(つまりフレーム)が変更されなくなるまで、すべてのセルのpreferredLayoutAttributesFittingAttributesが何度も検証されます。


2

上記の回答に加えて、

UICollectionViewFlowLayoutの見積もられたサイズプロパティをいくつかのサイズに設定し、sizeForItem:atIndexPathデリゲートメソッドを実装しないことを確認してください。

それでおしまい。


1

UICollectionViewDelegateFlowLayoutメソッドを実装する場合:

- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath

を呼び出すcollectionview performBatchUpdates:completion:と、サイズの高さがのsizeForItemAtIndexPath代わりに 使用されますpreferredLayoutAttributesFittingAttributes

のレンダリングプロセスはperformBatchUpdates:completionメソッドを通過preferredLayoutAttributesFittingAttributesしますが、変更は無視されます。


私はこの誤動作を見てきましたが、推定サイズがゼロの場合のみです。推定サイズを設定してもよろしいですか?
dgatwood 2017

1

それが助けるかもしれない誰にでも、

estimatedItemSize設定されている場合、私はその厄介なクラッシュがありました。で0を返してもnumberOfItemsInSection。したがって、セル自体とその自動レイアウトはクラッシュの原因ではありませんでした... collectionViewは、空であってもクラッシュしましたestimatedItemSizeが自己サイズ設定に設定されている。

私の場合、collectionViewを含むコントローラーからcollectionViewControllerにプロジェクトを再編成し、それが機能しました。

図を行きます。


1
collectionView.collectionViewLayout.invalidateLayout()後に電話してみてくださいcollectionView.reloadData()
チェンサム2017年

ios10以下では、このコードを追加します `override func viewWillLayoutSubviews(){super.viewWillLayoutSubviews()if #available(iOS 11.0、*){} else {mainCollectionView.collectionViewLayout.invalidateLayout()}}`
Marosdee Uma

1

運なしですべてを試した人にとって、これが私にとってそれを機能させた唯一のことです。セル内の複数行ラベルについては、この魔法の行を追加してみてください:

label.preferredMaxLayoutWidth = 200

詳細:こちら

乾杯!


0

上記のメソッド例はコンパイルされません。これは修正されたバージョンです(ただし、動作するかどうかはテストされていません)。

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
{
    let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes

    var newFrame = attr.frame
    self.frame = newFrame

    self.setNeedsLayout()
    self.layoutIfNeeded()

    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = desiredHeight
    attr.frame = newFrame
    return attr
}

0

詳細情報を更新します。

  • を使用する場合はflowLayout.estimatedItemSize、iOS8.3以降のバージョンを使用することをお勧めします。iOS8.3より前では、クラッシュし[super layoutAttributesForElementsInRect:rect];ます。エラーメッセージは

    *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

  • 第二に、iOS8.xバージョンでflowLayout.estimatedItemSizeは、異なるセクションインセット設定が機能しませんでした。ie関数:(UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:


0

ソリューションは4つの重要なステップで構成されています。

  1. 動的セルサイズ設定を有効にする

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

  1. 自動レイアウトが有効になっていない場合は、自動レイアウトを明示的に有効contentViewにし、セルの端に固定しcontentViewます。これは、自動レイアウトが有効になっていない場合に、自己サイズ設定を防止するためです。
class MultiLineCell: UICollectionViewCell{

    private var textViewHeightContraint: NSLayoutConstraint!

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentViewWidthAnchor = contentView.widthAnchor.constraint(equalToConstant: 0)

        NSLayoutConstraint.activate([
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
    } 
    ...
}
  1. contentView.widthAnchor.constraint from collectionView(:cellForItemAt:)を設定して、contentView の幅をcollectionViewの幅に制限します。

次のように発生します。我々は、細胞のセットでしょうmaxWidthにCollectionViewの幅に変数をcollectionView(:cellForItemAt:)し、中maxWidthdidSet方法、私たちはのmaxWidthにwidthAnchor.constantを設定します。

class ViewController: UIViewController, UICollectionViewDataSource {
    ...

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! MultiLineCell
        cell.textView.text = dummyTextMessages[indexPath.row]
        cell.maxWidth = collectionView.frame.width
        return cell
    }

    ...
}
class MultiLineCell: UICollectionViewCell{
    ....

    var maxWidth: CGFloat? {
        didSet {
            guard let maxWidth = maxWidth else {
                return
            }
            contentViewWidthAnchor.constant = maxWidth
            contentViewWidthAnchor.isActive = true
        }
    }

    ....
}

UITextViewのセルフサイズを有効にする必要があるため、追加の手順があります。

4. UITextViewのheightAnchor.constantを計算して設定します。

だから、contentViewの幅が設定されるたびに、私たちは中に沿ってUITextViewの高さを調整しますdidSetmaxWidth

UICollectionViewCell内:

var maxWidth: CGFloat? {
    didSet {
        guard let maxWidth = maxWidth else {
            return
        }
        contentViewWidthAnchor.constant = maxWidth
        contentViewWidthAnchor.isActive = true

        let sizeToFitIn = CGSize(width: maxWidth, height: CGFloat(MAXFLOAT))
        let newSize = self.textView.sizeThatFits(sizeToFitIn)
        self.textViewHeightContraint.constant = newSize.height
    }
}

これらの手順により、目的の結果が得られます。ここに完全な実行可能な要点があります

おかげヴァディムBulavin彼の明確でenlightingブログ記事について- コレクションビュー細胞セルフサイジング:ステップバイステップのチュートリアル


-2

使ってみましestimatedItemSizeたがestimatedItemSize、セルの高さと正確に一致しないセルを挿入したり削除したりすると、たくさんのバグがありました。estimatedItemSizeプロトタイプセルを使用して、ダイナミックセルの設定を中止して実装しました。方法は次のとおりです。

このプロトコルを作成します。

protocol SizeableCollectionViewCell {
    func fittedSize(forConstrainedSize size: CGSize)->CGSize
}

このプロトコルをカスタムに実装しますUICollectionViewCell

class YourCustomCollectionViewCell: UICollectionViewCell, SizeableCollectionViewCell {

    @IBOutlet private var mTitle: UILabel!
    @IBOutlet private var mDescription: UILabel!
    @IBOutlet private var mContentView: UIView!
    @IBOutlet private var mTitleTopConstraint: NSLayoutConstraint!
    @IBOutlet private var mDesciptionBottomConstraint: NSLayoutConstraint!

    func fittedSize(forConstrainedSize size: CGSize)->CGSize {

        let fittedSize: CGSize!

        //if height is greatest value, then it's dynamic, so it must be calculated
        if size.height == CGFLoat.greatestFiniteMagnitude {

            var height: CGFloat = 0

            /*now here's where you want to add all the heights up of your views.
              apple provides a method called sizeThatFits(size:), but it's not 
              implemented by default; except for some concrete subclasses such 
              as UILabel, UIButton, etc. search to see if the classes you use implement 
              it. here's how it would be used:
            */
            height += mTitle.sizeThatFits(size).height
            height += mDescription.sizeThatFits(size).height
            height += mCustomView.sizeThatFits(size).height    //you'll have to implement this in your custom view

            //anything that takes up height in the cell has to be included, including top/bottom margin constraints
            height += mTitleTopConstraint.constant
            height += mDescriptionBottomConstraint.constant

            fittedSize = CGSize(width: size.width, height: height)
        }
        //else width is greatest value, if not, you did something wrong
        else {
            //do the same thing that's done for height but with width, remember to include leading/trailing margins in calculations
        }

        return fittedSize
    }
}

コントローラをに準拠させUICollectionViewDelegateFlowLayout、その中に次のフィールドを含めます。

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout {
    private var mCustomCellPrototype = UINib(nibName: <name of the nib file for your custom collectionviewcell>, bundle: nil).instantiate(withOwner: nil, options: nil).first as! SizeableCollectionViewCell
}

データをバインドするプロトタイプセルとして使用され、そのデータが動的にしたいディメンションにどのように影響したかを決定します

最後に、UICollectionViewDelegateFlowLayout's collectionView(:layout:sizeForItemAt:)を実装する必要があります。

class YourViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {

    private var mDataSource: [CustomModel]

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath)->CGSize {

        //bind the prototype cell with the data that corresponds to this index path
        mCustomCellPrototype.bind(model: mDataSource[indexPath.row])    //this is the same method you would use to reconfigure the cells that you dequeue in collectionView(:cellForItemAt:). i'm calling it bind

        //define the dimension you want constrained
        let width = UIScreen.main.bounds.size.width - 20    //the width you want your cells to be
        let height = CGFloat.greatestFiniteMagnitude    //height has the greatest finite magnitude, so in this code, that means it will be dynamic
        let constrainedSize = CGSize(width: width, height: height)

        //determine the size the cell will be given this data and return it
        return mCustomCellPrototype.fittedSize(forConstrainedSize: constrainedSize)
    }
}

以上です。collectionView(:layout:sizeForItemAt:)この方法でセルのサイズを返すと、を使用する必要がなくなり、セルのestimatedItemSize挿入と削除は完全に機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.