如何使用FlowLayout确定UICollectionView的高度


118

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

问题是:即使我所有单元格的高度都固定且相等,我也不知道在中有多少“行” /行UICollectionView。我也无法通过数据源中的项目数来确定该计数,因为代表数据项的单元格的宽度不同,因此,在一行中的项目数也随之变化UICollectionView

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

Answers:


255

哇!出于某种原因,经过数小时的研究,我现在对我的问题找到了一个很简单的答案:我完全在错误的位置进行搜索,翻阅了我可以在上找到的所有文档UICollectionView

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


1
哇,我希望UITableView有类似的东西
Chris Chen

1
Ignatus,@ jbandi,Chris等人,您可以对此进行扩展吗?当我以较大的项目间距在水平滚动方向上进行测试(以便项目可以水平放置)时,集合视图返回无效的固有内容大小(-1,-1),而collectionViewContentSize返回有效的内容集合视图的边界-不考虑只有一行被空间包围。简而言之,它不是很内在的。谢谢!
克里斯·康诺弗

8
滚动视图的collectionViewContentSize和contentSize有什么区别?
rounak 2015年

当您在viewDidLoad中调用集合视图的contentSize时,它将返回集合视图的框架,collectionViewContentSize返回正确的内容大小。
Fury

1
它可以为谁提供帮助:在此之前,我必须先执行“ layoutIfNeeded()”,这样我才能使其工作。
asanli

37

如果您使用的是自动布局,则可以创建的子类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

11
不适用于我...我正在UITableViewCell内使用我的CollectionView,并且希望使用iOS8 UITableViewAutomaticDimension集合视图的高度增加时,tableview的高度会增加,但是我的collectionview仍然很小:(
Georg

太棒了 在UIScrollView中对我的UICollectionView的简短解决方案!
dotToString 2015年

2
重要的一件事是在集合视图中禁用滚动和滚动条
Georg

1
与Georg相同的问题,当它应该大于400时,它的高度约为50px
xtrinch

3
是的,我将collectionview设置为“ not scrollabe,no scollbars”,然后重新加载它,然后设置了tableview的内容。另外,collectionview是一个子类,该子类将其内容大小作为固有内容大小返回。希望有帮助!
Georg

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];

11

user1046037在Swift中回答...

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

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

它工作正常,但是如果您有类似UILabel上面的内容,那么将内容从collectionView上面的其他元素传递过来,您还有其他解决方案吗?
KolaCaine

11

swift 3代码为user1046037回答

import UIKit

class DynamicCollectionView: UICollectionView {

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

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

我是一个较新的人,我不确定在ViewController中的位置以及如何使用它,能否请您对答案进行编辑.....?
阿比拉米巴拉(Abirami Bala)

1
只需在Storyboard中将此类设置为您的collectionview
Mohammad Sadiq Shaikh

7

斯威夫特4

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

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

1
我发现它无法在iPhone 6/7 plus和XR,XS-MAX中运行
Rahul K Rajan'Oct

7

斯威夫特4.2

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

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

4

回答这个问题为时已晚,但是我最近经历了这个问题。
@lee的上述答案对我有很大帮助。但是这个答案仅限于Objective-C,而我正在迅速进行
@lee关于您的回答,请允许我让迅速的用户轻松解决此问题。为此,请按照以下步骤操作:

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

var height : CGFloat!

viewDidAppear你可以得到它:

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

也许当你 reload需要用新数据计算新高度时,可以通过以下方法获取它:addObserver侦听CollectionView完成的重载数据,网址为viewWillAppear

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")
}

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


@ShikhaSharma:您可以在“ ViewDidDisappear”方法中添加removeObserver代码,该代码仅在视图确认其消失后才删除观察者。请检查更新后的答案。
Er Vihar

收到此错误:收到但未处理-observeValueForKeyPath:ofObject:change:context:消息。
Shikha Sharma,

@ShikhaSharma:对不起,指定方法是我的错误。尝试将removeobserver代码放入viewWillDisappear中。
Er 维哈尔

1

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

     collectionView.rx.observe(CGSize.self , "contentSize").subscribe(onNext: { (size) in
        print("sizer \(size)")
    }).disposed(by: rx.disposeBag)

0

@ 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
    }
}

0

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)
}

之前

相对于超级视图的高度限制 不良的模拟器结果

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


0

假设您的collectionView被命名为collectionView,则可以按照以下方式进行调整。

let height = collectionView.collectionViewLayout.collectionViewContentSize.height

0

另外,您最好reloadData先打电话给身高:

theCollectionView.collectionViewLayout.collectionViewContentSize;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.