web-dev-qa-db-ja.com

UICollectionView-動的なセルの高さ?

高さが異なるcollectionViewCellsの束を表示する必要があります。ビューが複雑すぎるため、予想される高さを手動で計算したくありません。自動レイアウトを適用してセルの高さを計算したい

dequeueReusableCellWithReuseIdentifierの外でcellForItemAtIndexPathを呼び出すと、collectionViewが壊れてクラッシュします

別の問題は、セルが独立したxibにないため、一時的なセルを手動でインスタンス化して高さの計算に使用できないことです。

これに対する解決策はありますか?

public func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {

    var cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellIdentifier, forIndexPath: indexPath) as UICollectionViewCell
    configureCell(cell, item: items[indexPath.row])

    cell.contentView.setNeedsLayout()
    cell.contentView.layoutIfNeeded()

    return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
}

編集:

DequeueReusableCellWithReuseIdentifierが呼び出されるとすぐにクラッシュが発生します。そのメソッドを呼び出さずにサイズを返すと、すべてがうまく機能し、計算されたサイズなしでセルが表示されます

フローレイアウトでは負のサイズまたはゼロサイズはサポートされていません

2015-01-26 18:24:34.231 [13383:9752256] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001095aef35 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000109243bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x0000000109499f33 -[__NSArrayM objectAtIndex:] + 227
    3   UIKit                               0x0000000107419d9c -[UICollectionViewFlowLayout _getSizingInfos] + 842
    4   UIKit                               0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526
    5   UIKit                               0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257
    6   UIKit                               0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67
    7   UIKit                               0x00000001074301c6 -[UICollectionViewData layoutAttributesForItemAtIndexPath:] + 44
    8   UIKit                               0x00000001073fddb1 -[UICollectionView _dequeueReusableViewOfKind:withIdentifier:forIndexPath:viewCategory:] + 248
    9                                       0x00000001042b824c _TFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 700
    10                                     0x00000001042b83d4 _TToFC1228BasePaginatingViewController14collectionViewfS0_FTCSo16UICollectionView6layoutCSo22UICollectionViewLayout22sizeForItemAtIndexPathCSo11NSIndexPath_VSC6CGSize + 100
    11  UIKit                               0x0000000107419e2e -[UICollectionViewFlowLayout _getSizingInfos] + 988
    12  UIKit                               0x000000010741aca9 -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] + 526
    13  UIKit                               0x000000010741651f -[UICollectionViewFlowLayout prepareLayout] + 257
    14  UIKit                               0x000000010742da10 -[UICollectionViewData _prepareToLoadData] + 67
    15  UIKit                               0x000000010742e0e9 -[UICollectionViewData validateLayoutInRect:] + 54
    16  UIKit                               0x00000001073f67b8 -[UICollectionView layoutSubviews] + 170
    17  UIKit                               0x0000000106e3c973 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 521
    18  QuartzCore                          0x0000000106b0fde8 -[CALayer layoutSublayers] + 150
    19  QuartzCore                          0x0000000106b04a0e _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    20  QuartzCore                          0x0000000106b0487e _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    21  QuartzCore                          0x0000000106a7263e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    22  QuartzCore                          0x0000000106a7374a _ZN2CA11Transaction6commitEv + 390
    23  QuartzCore                          0x0000000106a73db5 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 89
    24  CoreFoundation                      0x00000001094e3dc7 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    25  CoreFoundation                      0x00000001094e3d20 __CFRunLoopDoObservers + 368
    26  CoreFoundation                      0x00000001094d9b53 __CFRunLoopRun + 1123
    27  CoreFoundation                      0x00000001094d9486 CFRunLoopRunSpecific + 470
    28  GraphicsServices                    0x000000010be869f0 GSEventRunModal + 161
    29  UIKit                               0x0000000106dc3420 UIApplicationMain + 1282
    30                                      0x000000010435c709 main + 169
    31  libdyld.dylib                       0x000000010a0f2145 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
55
aryaxt

私はUICollectionViewでこの問題に遭遇しましたが、上記の答えに似ていますが、純粋なUICollectionViewの方法でそれを解決した方法です。

  1. 動的にするために入力するものをすべて含むカスタムUICollectionViewCellを作成します。最も簡単なアプローチのように思えるので、私はそれのために独自の.xibを作成しました。

  2. セルを上から下に計算できるようにする制約を.xibに追加します。すべての高さを考慮していない場合、サイズ変更は機能しません。上部にビューがあり、その下にラベルがあり、その下に別のラベルがあるとします。セルの上部の制約をそのビューの上部に接続し、ビューの下部を最初のラベルの上部に、最初のラベルの下部を2番目のラベルの上部に、2番目のラベルの下部に接続する必要があります。セルの下部に。

  3. .xibをViewControllerにロードし、viewDidLoadのc​​ollectionViewに登録します

    let nib = UINib(nibName: CustomCellName, bundle: nil)
    self.collectionView!.registerNib(nib, forCellWithReuseIdentifier: "customCellID")`
    
  4. そのxibの2番目のコピーをクラスにロードし、それをプロパティとして保存して、それを使用してそのセルのサイズを決定できるようにします

    let sizingNibNew = NSBundle.mainBundle().loadNibNamed(CustomCellName, owner: CustomCellName.self, options: nil) as NSArray
    self.sizingNibNew = (sizingNibNew.objectAtIndex(0) as? CustomViewCell)!
    
  5. View ControllerにUICollectionViewFlowLayoutDelegateを実装します。重要なメソッドはsizeForItemAtIndexPathと呼ばれます。そのメソッド内では、indexPathからそのセルに関連付けられているデータソースからデータをプルする必要があります。次に、sizingCellを構成し、preferredLayoutSizeFittingSizeを呼び出します。このメソッドは、self.sizingCell.preferredLayoutSizeFittingSize(targetSize)から返されるコンテンツのインセットと高さを差し引いた幅で構成されるCGSizeを返します。

    override func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        guard let data = datasourceArray?[indexPath.item] else {
            return CGSizeZero
        }
        let sectionInset = self.collectionView?.collectionViewLayout.sectionInset
        let widthToSubtract = sectionInset!.left + sectionInset!.right
    
        let requiredWidth = collectionView.bounds.size.width
    
    
        let targetSize = CGSize(width: requiredWidth, height: 0)
    
        sizingNibNew.configureCell(data as! CustomCellData, delegate: self)
        let adequateSize = self.sizingNibNew.preferredLayoutSizeFittingSize(targetSize)
        return CGSize(width: (self.collectionView?.bounds.width)! - widthToSubtract, height: adequateSize.height)
     }
    
  6. カスタムセル自体のクラスでは、awakeFromNibをオーバーライドし、contentViewにサイズを柔軟にする必要があることを伝える必要があります。

     override func awakeFromNib() {
        super.awakeFromNib()
        self.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight]
     }
    
  7. カスタムセルのオーバーライドlayoutSubviews

     override func layoutSubviews() {
       self.layoutIfNeeded()
      }
    
  8. カスタムセルのクラスにpreferredLayoutSizeFittingSizeを実装します。これは、レイアウトされているアイテムに対してトリックを行う必要がある場所です。ラベルの場合、preferredMaxWidthを指定する必要があります。

    func preferredLayoutSizeFittingSize(_ targetSize: CGSize)-> CGSize {
    
        let originalFrame = self.frame
        let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
    
    
        var frame = self.frame
        frame.size = targetSize
        self.frame = frame
    
        self.setNeedsLayout()
        self.layoutIfNeeded()
        self.label.preferredMaxLayoutWidth = self.questionLabel.bounds.size.width
    
    
        // calling this tells the cell to figure out a size for it based on the current items set
        let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
    
        let newSize = CGSize(width:targetSize.width, height:computedSize.height)
    
        self.frame = originalFrame
        self.questionLabel.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
    
        return newSize
    }
    

これらすべての手順で、正しいサイズが得られます。 0またはその他のファンキーな数字を取得する場合、制約を適切に設定していません。

26
bolnad

Xibなしでコレクションビューセルの動的な高さを維持できます(ストーリーボードのみを使用)。

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

        NSAttributedString* labelString = [[NSAttributedString alloc] initWithString:@"Your long string goes here" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0]}];
        CGRect cellRect = [labelString boundingRectWithSize:CGSizeMake(cellWidth, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil];

        return CGSizeMake(cellWidth, cellRect.size.height);
}

IBのnumberOfLinesが0であることを確認してください。

15
Nasir

TL; DR:画像にスキャンダウンし、チェックアウト ここでプロジェクトを実行しています

私が見つけたよりシンプルなソリューションのために私の答えを更新します。

私の場合、幅を固定し、セルの高さを可変にしたかったのです。回転を処理し、多くの介入を必要としない、再利用可能なドロップインソリューションが必要でした。

私がたどり着いたのは、コレクションセル(この場合は基本クラス)の(ちょうど)systemLayoutFitting(...)をオーバーライドし、最初に制約を追加してcontentViewに間違ったディメンションを設定するUICollectionViewの努力を無効にすることでした既知の寸法、この場合は幅。

class EstimatedWidthCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.translatesAutoresizingMaskIntoConstraints = false
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        contentView.translatesAutoresizingMaskIntoConstraints = false
    }

    override func systemLayoutSizeFitting(
        _ targetSize: CGSize, withHorizontalFittingPriority
        horizontalFittingPriority: UILayoutPriority,
        verticalFittingPriority: UILayoutPriority) -> CGSize {

        width.constant = targetSize.width

そして、セルの最終サイズを返します-セル自体の次元に使用されますが(これはバグのように感じます)、contentViewではありません-それ以外の場合は競合するサイズに制限されます(したがって、上記の制約)。正しいセルサイズを計算するために、フロートするディメンションの優先順位を低くし、修正する幅内にコンテンツを収めるために必要な高さを取得します。

        let size = contentView.systemLayoutSizeFitting(
            CGSize(width: targetSize.width, height: 1),
            withHorizontalFittingPriority: .required,
            verticalFittingPriority: verticalFittingPriority)

        print("\(#function) \(#line) \(targetSize) -> \(size)")
        return size
    }

    lazy var width: NSLayoutConstraint = {
        return contentView.widthAnchor
            .constraint(equalToConstant: bounds.size.width)
            .isActive(true)
    }()
}

しかし、この幅はどこから来たのでしょうか?コレクションビューのフローレイアウトのestimatedItemSizeを介して構成されます。

lazy var collectionView: UICollectionView = {
    let view = UICollectionView(frame: CGRect(), collectionViewLayout: layout)
    view.backgroundColor = .cyan
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()

lazy var layout: UICollectionViewFlowLayout = {
    let layout = UICollectionViewFlowLayout()
    let width = view.bounds.size.width // should adjust for inset
    layout.estimatedItemSize = CGSize(width: width, height: 10)
    layout.scrollDirection = .vertical
    return layout
}()

最後に、回転を処理するために、trailCollectionDidChangeを実装してレイアウトを無効にします。

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    layout.estimatedItemSize = CGSize(width: view.bounds.size.width, height: 10)
    layout.invalidateLayout()
    super.traitCollectionDidChange(previousTraitCollection)
}

最終結果は次のようになります。

enter image description here

そして、私は ここに実際のサンプル を公開しました

10
Chris Conover

Swift 4。*

UICollectionViewCellのXibを作成しましたが、これは適切なアプローチのようです。

extension ViewController: UICollectionViewDelegateFlowLayout {

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

    private func size(for indexPath: IndexPath) -> CGSize {
        // load cell from Xib
        let cell = Bundle.main.loadNibNamed("ACollectionViewCell", owner: self, options: nil)?.first as! ACollectionViewCell

        // configure cell with data in it
        let data = self.data[indexPath.item]
        cell.configure(withData: data)

        cell.setNeedsLayout()
        cell.layoutIfNeeded()

        // width that you want
        let width = collectionView.frame.width
        let height: CGFloat = 0

        let targetSize = CGSize(width: width, height: height)

        // get size with width that you want and automatic height
        let size = cell.contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .defaultHigh, verticalFittingPriority: .fittingSizeLevel)
        // if you want height and width both to be dynamic use below
        // let size = cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)

        return size
    }
}

#note:このサイズを決定するケースでデータを構成するときに画像を設定することはお勧めしません。歪んだ/望ましくない結果になりました。テキストを設定しても、以下の結果しか得られませんでした。

enter image description here

6
Dari

それは非常に人気のある質問のようですので、私は謙虚な貢献をしようとします。


以下のコードはSwift 4ストーリーボードなしのセットアップのソリューションです。以前の回答からのいくつかのアプローチを利用するため、デバイスの回転に起因する自動レイアウト警告を防ぎます。

コードサンプルが少し長い場合は申し訳ありません。 StackOverflowによって完全にホストされる「使いやすい」ソリューションを提供したいと思います。投稿に対する提案がある場合は、アイデアを共有してください。それに応じて更新します。

セットアップ:

2つのクラス:ViewController.SwiftおよびMultilineLabelCell.Swift-単一のUILabelを含むセル。

MultilineLabelCell.Swift

import UIKit

class MultilineLabelCell: UICollectionViewCell {
    static let reuseId = "MultilineLabelCellReuseId"

    private let label: UILabel = UILabel(frame: .zero)

    override init(frame: CGRect) {
        super.init(frame: frame)

        layer.borderColor = UIColor.red.cgColor
        layer.borderWidth = 1.0

        label.numberOfLines = 0
        label.lineBreakMode = .byWordWrapping

        let labelInset = UIEdgeInsets(top: 10, left: 10, bottom: -10, right: -10)
        contentView.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        label.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor, constant: labelInset.top).isActive = true
        label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor, constant: labelInset.left).isActive = true
        label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor, constant: labelInset.right).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor, constant: labelInset.bottom).isActive = true

        label.layer.borderColor = UIColor.black.cgColor
        label.layer.borderWidth = 1.0
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("Storyboards are quicker, easier, more seductive. Not stronger then Code.")
    }

    func configure(text: String?) {
        label.text = text
    }

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.left
        layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
        return layoutAttributes
    }
}

ViewController.Swift

import UIKit

let samuelQuotes = [
    "Samuel says", 
    "Add different length strings here for better testing"
]

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    private(set) var collectionView: UICollectionView

    // Initializers
    init() {
        // Create new `UICollectionView` and set `UICollectionViewFlowLayout` as its layout
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        // Create new `UICollectionView` and set `UICollectionViewFlowLayout` as its layout
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Dynamic size sample"

        // Register Cells
        collectionView.register(MultilineLabelCell.self, forCellWithReuseIdentifier: MultilineLabelCell.reuseId)

        // Add `coolectionView` to display hierarchy and setup its appearance
        view.addSubview(collectionView)
        collectionView.backgroundColor = .white
        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

        // Setup Autolayout constraints
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
        collectionView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
        collectionView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true

        // Setup `dataSource` and `delegate`
        collectionView.dataSource = self
        collectionView.delegate = self

        (collectionView.collectionViewLayout as! UICollectionViewFlowLayout).estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        (collectionView.collectionViewLayout as! UICollectionViewFlowLayout).sectionInsetReference = .fromLayoutMargins
    }

    // MARK: - UICollectionViewDataSource -
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MultilineLabelCell.reuseId, for: indexPath) as! MultilineLabelCell
        cell.configure(text: samuelQuotes[indexPath.row])
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return samuelQuotes.count
    }

    // MARK: - UICollectionViewDelegateFlowLayout -
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let sectionInset = (collectionViewLayout as! UICollectionViewFlowLayout).sectionInset
        let referenceHeight: CGFloat = 100 // Approximate height of your cell
        let referenceWidth = collectionView.safeAreaLayoutGuide.layoutFrame.width
            - sectionInset.left
            - sectionInset.right
            - collectionView.contentInset.left
            - collectionView.contentInset.right
        return CGSize(width: referenceWidth, height: referenceHeight)
    }
}

このサンプルを実行するには、新しいXcodeプロジェクトを作成し、対応するファイルを作成して、AppDelegateの内容を次のコードに置き換えます。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var navigationController: UINavigationController?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow(frame: UIScreen.main.bounds)

        if let window = window {
            let vc = ViewController()
            navigationController = UINavigationController(rootViewController: vc)
            window.rootViewController = navigationController
            window.makeKeyAndVisible()
        }

        return true
    }
}
5
fewlinesofcode

このSOに記載されている手順に従いましたが、コレクションビューのデータ(テキスト)の幅が十分に広い場合を除いて、すべて問題ありません。 systemLyaoutSizeFittingSizeのドキュメントを確認すると、この解決策があるので、要求したとおりにセルが幅を占有します。

- (CGSize)calculateSizeForSizingCell:(UICollectionViewCell *)sizingCell width:(CGFloat)width {
    CGRect frame = sizingCell.frame;
    frame.size.width = width;
    sizingCell.frame = frame;
    [sizingCell setNeedsLayout];
    [sizingCell layoutIfNeeded];

    CGSize size = [sizingCell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize
                        withHorizontalFittingPriority:UILayoutPriorityRequired
                              verticalFittingPriority:UILayoutPriorityFittingSizeLevel];
    return size;
}

これが誰かを助けることを願っています。

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0);

Appleドキュメント:

-systemLayoutSizeFittingSize:withHorizo​​ntalFittingPriority:verticalFittingPriority:を両方の優先度のUILayoutPriorityFittingSizeLevelで送信することと同等です。

Appleのドキュメントによると、デフォルト値は「かなり低い」ですが、

-[UIView systemLayoutSizeFittingSize:]を送信すると、ターゲットサイズ(引数)に最も近いサイズが計算されます。 UILayoutPriorityFittingSizeLevelは、ビューがその計算のターゲットサイズに準拠する優先レベルです。かなり低いです。通常、この優先度で制約を作成することは適切ではありません。高くしたい、または低くしたい。

したがって、デフォルトの動作を変更すると、UILayoutPriorityRequiredで幅(水平方向のフィッティング)が強制されます。

1
John Pang

Bolnad answer をステップ4まで実行します。

次に、他のすべての手順を次のように置き換えて、より簡単にします。

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

    // Configure your cell
    sizingNibNew.configureCell(data as! CustomCellData, delegate: self)

    // We use the full width minus insets
    let width = collectionView.frame.size.width - collectionView.sectionInset.left - collectionView.sectionInset.right

    // Constrain our cell to this width 
    let height = sizingNibNew.systemLayoutSizeFitting(CGSize(width: width, height: .infinity), withHorizontalFittingPriority: UILayoutPriorityRequired, verticalFittingPriority: UILayoutPriorityFittingSizeLevel).height

    return CGSize(width: width, height: height)
}
1
Rivera

それは私のために働いた、あなたも願っています。

*注意:Nibで自動レイアウトを使用しましたが、contentViewのサブビューに上下の制約を追加することを忘れないでください

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
           let cell = YourCollectionViewCell.instantiateFromNib()
           cell.frame.size.width = collectionView.frame.width
           cell.data = viewModel.data[indexPath.item]
           let resizing = cell.systemLayoutSizeFitting(UILayoutFittingCompressedSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.fittingSizeLevel)
           return resizing
    }
0
logan.Nguyen

@ mbm29414からの有益な回答に基づくSwift 4の回答。

残念ながら、XIBファイルを使用する必要があります。代替手段はないようです。

重要な部分は、サイズ変更セル(一度だけ作成)を使用し、コレクションビューを初期化するときにXIBを登録することです。

次に、sizeForItemAt関数内で各セルのサイズを動的に変更します。

// UICollectionView Vars and Constants
let CellXIBName = YouViewCell.XIBName
let CellReuseID = YouViewCell.ReuseID
var sizingCell = YouViewCell()


fileprivate func initCollectionView() {
    // Connect to view controller
    collectionView.dataSource = self
    collectionView.delegate = self

    // Register XIB
    collectionView.register(UINib(nibName: CellXIBName, bundle: nil), forCellWithReuseIdentifier: CellReuseID)

    // Create sizing cell for dynamically sizing cells
    sizingCell = Bundle.main.loadNibNamed(CellXIBName, owner: self, options: nil)?.first as! YourViewCell

    // Set scroll direction
    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical
    collectionView.collectionViewLayout = layout

    // Set properties
    collectionView.alwaysBounceVertical = true
    collectionView.alwaysBounceHorizontal = false

    // Set top/bottom padding
    collectionView.contentInset = UIEdgeInsets(top: collectionViewTopPadding, left: collectionViewSidePadding, bottom: collectionViewBottomPadding, right: collectionViewSidePadding)

    // Hide scrollers
    collectionView.showsVerticalScrollIndicator = false
    collectionView.showsHorizontalScrollIndicator = false
}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    // Get cell data and render post
    let data = YourData[indexPath.row]
    sizingCell.renderCell(data: data)

    // Get cell size
    sizingCell.setNeedsLayout()
    sizingCell.layoutIfNeeded()
    let cellSize = sizingCell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)

    // Return cell size
    return cellSize
}
0
Crashalot