133

我有一个UICollectionViewwith UICollectionViewFlowLayout,我想计算它的内容大小(用于intrinsicContentSize通过 AutoLayout 调整其高度所需的返回)。

问题是:即使我对所有单元格都有一个固定且相等的高度,我也不知道UICollectionView. 我也无法通过数据源中的项目数确定该计数,因为表示数据项的单元格的宽度不同,因此我在UICollectionView.

由于我在官方文档中找不到有关此主题的任何提示,并且谷歌搜索并没有给我带来任何进一步的帮助,因此非常感谢任何帮助和想法。

4

13 回答 13

271

哇!出于某种原因,经过数小时的研究,我现在找到了一个非常简单的问题答案:我完全在错误的地方搜索,翻遍了我能找到的所有文档UICollectionView

简单易行的解决方案在于底层布局:只需调用collectionViewContentSize您的myCollectionView.collectionViewLayout属性,即可获得内容的高度和宽度为CGSize. 就这么简单。

于 2012-12-09T15:09:55.207 回答
42

如果您使用的是Auto layout,那么您可以创建一个子类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
于 2014-10-07T08:53:47.613 回答
25

viewDidAppear可以通过以下方式获得它:

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

也许当您重新加载数据然后需要使用新数据计算新高度时,您可以通过以下方式获得它:添加观察者以在您CollectionView完成重新加载数据时收听viewdidload

[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];
于 2015-02-07T04:40:53.703 回答
13

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
    }

}
于 2016-11-06T06:33:10.767 回答
12

user1046037 用 Swift 回答...

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

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}
于 2016-04-20T15:09:34.917 回答
9

斯威夫特 4

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

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}
于 2018-03-23T03:01:15.847 回答
9

斯威夫特 4.2

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

    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}
于 2019-01-09T17:43:57.877 回答
6

现在回答这个问题为时已晚,但我最近经历了这个问题。
@lee上面的答案对我得到答案有很大帮助。但是这个答案仅限于Objective-c并且我正在swift中工作。
@lee参考您的回答,请允许我让快速用户轻松解决此问题。为此,请按照以下步骤操作:

在声明部分声明一个CGFloat变量:

var height : CGFloat!

viewDidAppear可以通过以下方式获得它:

height = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

也许当您reload的数据然后需要用新数据计算新高度时,您可以通过以下方式获得它:在您完成重新加载数据时addObserver收听:CollectionViewviewWillAppear

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)
        ....
        ....
        self.shapeCollectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old, context: nil)
    }

然后添加波纹管功能以获得新的高度或collectionview完成重新加载后做任何事情:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

        var frame : CGRect! = self.myCollectionView.frame
        frame.size.height = newHeight

        self.myCollectionView.frame = frame
    }

并且不要忘记删除观察者:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)
    ....
    ....
    self.myCollectionView.removeObserver(self, forKeyPath: "contentSize")
}

我希望这将帮助您迅速解决您的问题。

于 2017-04-19T05:38:30.323 回答
2

这个答案基于@Er。维哈尔的回答。您可以使用 RxSwift 轻松实现该解决方案

     collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in
        print("sizer \(size)")
    }).disposed(by: rx.disposeBag)
于 2019-10-21T08:24:24.670 回答
1

@user1046037 的 Swift 版本:

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
    }
}
于 2018-03-01T17:28:11.070 回答
0

斯威夫特 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)
}

相对于超级视图的高度约束 模拟器结果不好

相对于安全区域的高度限制 良好的模拟器结果

于 2019-05-06T17:54:38.287 回答
0

In addition,you'd better calling reloadData before getting the height:

theCollectionView.collectionViewLayout.collectionViewContentSize;
于 2018-10-29T03:25:07.507 回答
0

假设您的 collectionView 被命名为collectionView您可以采用如下高度。

let height = collectionView.collectionViewLayout.collectionViewContentSize.height
于 2019-12-11T16:44:10.087 回答