如何使用 FlowLayout 确定 UICollectionView 的高度

Posted

技术标签:

【中文标题】如何使用 FlowLayout 确定 UICollectionView 的高度【英文标题】:How to determine height of UICollectionView with FlowLayout 【发布时间】:2012-11-27 03:11:10 【问题描述】:

我有一个UICollectionView 和一个UICollectionViewFlowLayout,我想计算它的内容大小(返回intrinsicContentSize 需要通过自动布局调整其高度)。

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

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

【问题讨论】:

【参考方案1】:

另外,你最好在得到高度之前打电话给reloadData

theCollectionView.collectionViewLayout.collectionViewContentSize;

【讨论】:

【参考方案2】:

假设您的collectionView 被命名为collectionView,您可以按如下方式获取高度。

let height = collectionView.collectionViewLayout.collectionViewContentSize.height

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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)

之前

之后

【讨论】:

【参考方案5】:

斯威夫特 4.2

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

    override var intrinsicContentSize: CGSize 
        return self.contentSize
    

【讨论】:

【参考方案6】:

斯威夫特 4

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

  override var intrinsicContentSize: CGSize 
    return collectionViewLayout.collectionViewContentSize
  

【讨论】:

我发现它不适用于 iPhone 6/7 plus 和 XR,XS-MAX【参考方案7】:

@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
    

【讨论】:

【参考方案8】:

现在回答这个问题为时已晚,但我最近经历了这个问题。@lee 的above answer 帮助我得到了很多答案。但这个答案仅限于 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")

我希望这能帮助您快速解决问题。

【讨论】:

@ShikhaSharma:您可以在“ViewDidDisappear”方法中添加 removeObserver 代码,只有在视图确认其消失后才会删除观察者。请检查更新的答案。 收到此错误:An -observeValueForKeyPath:ofObject:change:context: 消息已收到但未处理。 @ShikhaSharma:抱歉,指定方法是我的错误。尝试将 removeobserver 代码放在 viewWillDisappear 中。 刚刚使用了它,它工作得很好——它的优点是在屏幕实际显示给用户之前通知我的回调,这样我就可以根据正在渲染的 UICollectionView 的最终大小。【参考方案9】:

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
    


【讨论】:

我是个新手,我不确定在我的 ViewController 中的何处以及如何使用它,您能否用答案进行编辑.....? 只需在 Storyboard 中将此类设置为您的收藏视图【参考方案10】:

user1046037 用 Swift 回答...

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

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

【讨论】:

它工作得很好,但是如果你有类似上面UILabel 的内容,来自collectionView 的内容会通过上面的其他元素,你还有其他解决方案吗?【参考方案11】:

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

【讨论】:

【参考方案12】:

如果您使用自动布局,那么您可以创建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

【讨论】:

对我不起作用...我在 UITableViewCell 中使用我的 CollectionView 并希望随着使用 ios8 UITableViewAutomaticDimension 的集合视图的高度增长,tableview 的高度也会增长但我的 collectionview 仍然很小: ( 太棒了。在 UIScrollView 内对我的 UICollectionView 进行简短而甜蜜的解决方案! 一个重要的事情是在集合视图中禁用滚动和滚动条 和Georg一样的问题,高约50px,应该超过400 是的,我将 collectionview 设置为“不滚动,没有 scollbars”,然后重新加载它,然后设置 tableview 的内容。此外,collectionview 是一个子类,它返回它的内容大小作为 intrinsicContentSize。希望有帮助!【参考方案13】:

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

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

【讨论】:

哇,我希望 UITableView 有类似的东西 Ignatus, @jbandi, Chris 等人,你能扩展一下吗?当我使用具有较大项目间距的水平滚动方向进行测试时(以便项目水平布局),集合视图返回了无效的内在内容大小(-1,-1),而 collectionViewContentSize 返回了有效的内容集合视图的边界 - 不考虑只有一行被空间包围。简而言之,它不是很内在。谢谢! 滚动视图的collectionViewContentSize和contentSize有什么区别? 对谁有帮助:在此之前我必须做一个“layoutIfNeeded()”,这样我才能让它工作。 collectionViewContentSize 什么时候设置?我们什么时候可以从中读取?

以上是关于如何使用 FlowLayout 确定 UICollectionView 的高度的主要内容,如果未能解决你的问题,请参考以下文章

如何使用默认 FlowLayout 在 JPanel 中将 JButton 居中?

如何在collectionView中重新加载补充视图

UITableViewCell 中的 UICollectionView?

如何将我的 UICollectionView Flowlayout 更改为具有水平滚动的垂直列表

关于JAVA的FlowLayout流动布局的换行问题--图形界面

适用于 Windows 应用商店应用的带有 FlowLayout 的 GridView