web-dev-qa-db-ja.com

FlowLayoutでUICollectionViewの高さを決定する方法

UICollectionViewを持つUICollectionViewFlowLayoutがあり、そのコンテンツサイズを計算したい(AutoLayoutで高さを調整するために必要なintrinsicContentSizeを返すため)。

問題は、すべてのセルの高さが同じで固定されていても、UICollectionViewにある「行」/行の数がわかりません。また、データアイテムを表すセルの幅が異なるため、データソース内のアイテムの数でそのカウントを決定することもできません。したがって、UICollectionViewの1行にあるアイテムの数も異なります。

公式ドキュメントでこのトピックに関するヒントを見つけることができなかったため、グーグルでこれ以上説明しなかったため、ヘルプやアイデアをいただければ幸いです。

109
Ignatius Tremor

おっ!なんらかの理由で、数時間の調査の後、私の質問に対する非常に簡単な答えを見つけました。UICollectionViewで見つけることができるすべてのドキュメントを掘り下げて、間違った場所で完全に検索していました。

シンプルで簡単な解決策は、基礎となるレイアウトにあります。myCollectionView.collectionViewLayoutプロパティでcollectionViewContentSizeを呼び出すだけで、コンテンツの高さと幅をCGSizeとして取得できます。それは簡単です。

242
Ignatius Tremor

自動レイアウトを使用している場合、UICollectionViewのサブクラスを作成できます

以下のコードを使用する場合、コレクションビューの内容に基づいて変化するため、コレクションビューの高さの制約を指定する必要はありません。

以下に実装を示します。

@interface DynamicCollectionView : UICollectionView

@end

@implementation DynamicCollectionView

- (void) layoutSubviews
{
    [super layoutSubviews];

    if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize]))
    {
        [self invalidateIntrinsicContentSize];
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize intrinsicContentSize = self.contentSize;

    return intrinsicContentSize;
}

@end
35
user1046037

viewDidAppearでは、次の方法で入手できます。

float height = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;

データをリロードし、新しいデータで新しい高さを計算する必要がある場合は、次の方法で取得できます:CollectionViewviewdidloadでデータのリロードを終了したときにリッスンするオブザーバーを追加します

[self.myCollectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

次に、以下の関数を追加して、新しい高さを取得するか、collectionviewのリロードが完了した後に何かを実行します。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    //Whatever you do here when the reloadData finished
    float newHeight = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;    
}

そして、オブザーバーを削除することを忘れないでください:

[self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];
20
lee

swiftでuser1046037の答え...

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize() {
            invalidateIntrinsicContentSize()
        }
    }

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}
11
Nick Wood

User1046037の回答のSwift 3コード

import UIKit

class DynamicCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
            self.invalidateIntrinsicContentSize()
        }

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

スイフト4

class DynamicCollectionView: UICollectionView {
  override func layoutSubviews() {
    super.layoutSubviews()
    if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
      self.invalidateIntrinsicContentSize()
    }
  }

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}
5
Wilson

Swift 4.2

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}
2
Cesare

Swift 4.2、Xcode 10.1、iOS 12.1:

何らかの理由で、collectionView.contentSize.heightがコレクションビューの解決された高さよりも小さく表示されていました。最初に、スーパービューの高さの1/2に対して自動レイアウト制約を使用していました。 これを修正するために、ビューの「安全な領域」に関連するように制約を変更しました。

これにより、collectionView.contentSize.heightを使用してコレクションビューに合わせてセルの高さを設定できました。

private func setCellSize() {
    let height: CGFloat = (collectionView.contentSize.height) / CGFloat(numberOfRows)
    let width: CGFloat = view.frame.width - CGFloat(horizontalCellMargin * 2)

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: width, height: height)
}

Before

height constraint relative to superviewbad simulator result

After

height constraint relative to safe areagood simulator result

0
Andrew Kirna

@ user1046037の迅速なバージョン:

public class DynamicCollectionView: UICollectionView {

    override public func layoutSubviews() {
        super.layoutSubviews()
        if !bounds.size.equalTo(intrinsicContentSize) {
            invalidateIntrinsicContentSize()
        }
    }

    override public var intrinsicContentSize: CGSize {
        let intrinsicContentSize: CGSize = contentSize
        return intrinsicContentSize
    }
}
0
Joshua Hart

さらに、高さを取得する前にreloadDataを呼び出した方がよいでしょう:myCollectionView.collectionViewLayout.collectionViewContentSize

0
guozqzzu