【问题标题】:How to adjust height of UICollectionView to be the height of the content size of the UICollectionView?如何将 UICollectionView 的高度调整为 UICollectionView 内容大小的高度?
【发布时间】:2017-07-15 05:53:54
【问题描述】:

我希望 UICollectionView(红色的)在这种情况下 UICollectionViewCells(黄色的)缩小到内容大小的高度,因为有很多空白空间。我尝试的是使用:

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

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

return self.collection.contentSize 总是返回 (width, 0) 并且由于这个原因,它缩小到高度 30 的值(我在 XIB 文件中为高度设置的值,尽管我有约束 >= 30)。

【问题讨论】:

  • 你必须采取ColletionView的高度约束出口并像这样调整高度heightConstraint.constant = collectionView.contentSize.height
  • 您使用的是自定义布局还是苹果提供的 - UICollectionViewFlowLayout?
  • 我使用的是默认的 UICollectionViewFlowLayout
  • 您应该使用 collectionViewLayout.collectionViewContentSize,请参阅@hasan83 的回答。这对你来说是一种可能性。

标签: ios swift uicollectionview nslayoutconstraint ios-autolayout


【解决方案1】:

首先计算单元格的数量,然后将其乘以单元格的高度,然后在此方法中返回高度

    collectionView.frame = CGRectMake (x,y,w,collectionView.collectionViewLayout.collectionViewContentSize.height); //objective c
    //[collectionView reloadData];
   collectionView.frame = CGRect(x: 0, y: 0, width: width, height: collectionView.collectionViewLayout.collectionViewContentSize.height) // swift

【讨论】:

  • 此方法返回索引路径中特定单元格的大小,如果您将高度设置为 noOfCell*heightOfCell 这将设置所有单元格的高度。
  • @Georgi Boyadzhiev,这是真的。
【解决方案2】:

我建议如下:

  1. 为您的集合视图添加高度限制。
  2. 将其优先级设置为 999。
  3. 将其常量设置为使其在情节提要上合理可见的任何值。
  4. 将集合视图的底部相等约束更改为大于或等于。
  5. 将高度限制连接到插座。
  6. 每次在集合视图上重新加载数据时,请执行以下操作:

您可能还想通过将集合视图的 Inset 添加到内容大小来考虑它。

代码示例:

CGFloat height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
heightConstraint.constant = height
self.view.setNeedsLayout() Or self.view.layoutIfNeeded()

说明:额外,看懂就不用看了。很明显!!

无论优先级如何,UI 都会尝试反映所有约束。由于存在 (999) 优先级较低的高度约束,以及大于或等于类型的底部约束。每当高度约束常量设置为小于父视图高度的值时,集合视图将等于给定高度,实现这两个约束。

但是,当高度约束常量设置为大于父视图高度的值时,两个约束都无法实现。因此,只有优先级较高的约束才会被实现,即大于或等于底部约束。

以下只是经验的猜测。因此,它实现了一个常数。但是,它还尝试使结果 UI 中的 错误 为其他未实现的较低优先级约束尽可能低。因此,集合视图高度将等于父视图大小。

【讨论】:

  • 我的有一定程度的扩展,但并不完美。它错过了大约 3 4 行标签。任何想法?这是它的外观:ibb.co/jeO9Rvibb.co/iV8b6v
  • @Nil 您的收藏视图中有页眉或页脚吗?必须添加页脚和页眉的高度。
  • 没关系,它奏效了。高度约束被设置为比例高度,这就是为什么只设置常数不会产生太大影响。
  • 在我们讨论的过程中,您知道如何将单元格固定到顶部吗?在我的水平集合视图中,一些单元格没有固定在顶部。只是随意的飘在中间。这里:ibb.co/fHbSwv
  • 和 cmets 中的其他人一样,使用这种方法时我的身高有所增加。为了解决这个问题,我简单地做了一个.reloadData(),然后在集合视图performBatchUpdates 中更改了高度约束的常数。
【解决方案3】:

您必须将高度限制设置为等于内容大小

HeightConstraint.constant = collection.contentSize.height 

【讨论】:

    【解决方案4】:

    跟随。

    1. UICollectionView 的第一个设置高度约束
    2. 这里calendarBaseViewHeightUICollectionView 高度变量
    3. 重新加载集合视图后调用该函数

      func resizeCollectionViewSize(){  
             calendarBaseViewHeight.constant = collectionView.contentSize.height      
       }
      

    【讨论】:

      【解决方案5】:

      我最终通过继承 UICollectionView 并覆盖以下一些方法。

      • intrinsicContentSize 返回self.collectionViewLayout.collectionViewContentSize 确保始终具有正确的大小
      • 然后只要它可能发生变化就调用它(比如reloadData

      代码:

      override func reloadData() {
          super.reloadData()
          self.invalidateIntrinsicContentSize()
      }
      
      override var intrinsicContentSize: CGSize {
          return self.collectionViewLayout.collectionViewContentSize
      }
      

      但请注意,如果您显示大量数据集,即使它们不适合屏幕,您也会失去“细胞重复使用”。

      【讨论】:

      • 这可以使UICollectionView 最初是其内容的大小,但如果内容发生变化,即:插入或删除行,它不会调整大小。如果我调用重新加载数据,则集合视图在调整大小时会结结巴巴。有什么办法可以阻止这种口吃?
      • 您是否尝试在不调用reloadData() 的情况下调用self.invalidateIntrinsicContentSize()?也许这可以工作。
      • 迄今为止最漂亮的解决方案——至少对于我的用例而言。谢谢你。它解决了时间问题,布局需要所有其他约束和视图的大小来确定内容大小 - 这个就可以了。
      • 实际上 - 我必须将内在大小限制为至少 1 - 从不 0 - 才能正常工作。否则计算时会出现小错误,将视图底部截断几个像素。
      • @AnthonyMDev 因为使用这种方法,您正在调整集合视图的大小以同时适合所有项目,因此所有项目都可以随时加载。
      【解决方案6】:

      1) 设置 CollectionView 的固定高度。

      2) 创建此 CollectionView 高度常量的 Outlet。 喜欢:

      IBOutlet NSLayoutConstraint *constHeight;

      3) 在您的 .m 文件中添加以下方法:

      - (void)viewDidLayoutSubviews {
          [super viewDidLayoutSubviews];
      
          CGFloat height = collectionMenu.collectionViewLayout.collectionViewContentSize.height;
          constHeight.constant = height;
      }
      

      【讨论】:

      • 这工作使用Swift 5Xcode 10.2.1。谢谢。
      【解决方案7】:

      在您的UICollectionView 上设置您的约束条件,例如TrailingLeadingBottom

      如果您更详细地查看我的高度限制,因为它纯粹是为了故事板外观,所以我没有收到错误,我有它 Remove at build time。真正的高度限制在我下面的代码中设置。

      我的DrawerCollectionView的代码,设置为集合视图Custom Class

      import UIKit
      
      class DrawerCollectionView: UICollectionView {
      
          override func didMoveToSuperview() {
              super.didMoveToSuperview()
      
              heightAnchor.constraint(equalToConstant: contentSize.height).isActive = true
          }
      }
      

      【讨论】:

      • 试过了,但这对我不起作用。 d4Rk 的另一个子类化解决方案进行了少量修改
      【解决方案8】:

      在 Swift 5 和 Xcode 10.2.1 中

      1. 修复 CollectionView 的高度

      2. 创建 CollectionViewHeight 的 Outlet

        IBOutlet weak var myCollectionViewHeight: NSLayoutConstraint!
        
      3. 使用下面的代码

        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            let height = myCollectionView.collectionViewLayout.collectionViewContentSize.height
            myCollectionViewHeight.constant = height
            self.view.layoutIfNeeded()
        }
        

      基于文本内容的单元格动态宽度...

      Dynamic cell width of UICollectionView depending on label width

      【讨论】:

      • myCollectionView 在你的代码中有什么作用。你声明的地方
      • @ Karthi Rasu,myCollectionView 是您的收藏视图高度约束。如果您使用故事板修复 collectionView 的高度。
      • 完美解决方案!!
      • 单元格的动态宽度...stackoverflow.com/questions/23134986/…
      【解决方案9】:

      为我工作

          let heightRes = resCollectionView.collectionViewLayout.collectionViewContentSize.height
          foodHeightConstrant.constant = height.advanced(by: 1 )
      
          foodCollectionView.setNeedsLayout()
          foodCollectionView.layoutIfNeeded()
      

      【讨论】:

        【解决方案10】:

        d4Rk 采用了很棒的解决方案,但在我的情况下,它会一直切断我的收藏视图的底部(太短)。我发现这是因为内在内容大小有时为 0,这会导致计算失败。身份证。我只知道这是修复它。

        import UIKit
        
        class SelfSizedCollectionView: UICollectionView {
            override func reloadData() {
                super.reloadData()
                self.invalidateIntrinsicContentSize()
            }
        
            override var intrinsicContentSize: CGSize {
                let s = self.collectionViewLayout.collectionViewContentSize
                return CGSize(width: max(s.width, 1), height: max(s.height,1))
            }
        
        }
        

        【讨论】:

          【解决方案11】:

          获取单元格的高度。像这样的

          let cellHeight = cell.frame.height
          

          获取集合视图的来源

          let cvOrigin = collectionView.frame.origin
          

          获取集合视图的宽度

          let cvWidth = collectionView.bounds.width
          

          设置内容视图的框架

          collection.frame = CGRect(x: cvOrigin.x, y: cvOrigin.y, width: cvWidth, height: cellHeight )
          

          【讨论】:

            【解决方案12】:

            这对我来说似乎是最简单的解决方案。

            class SelfSizingCollectionView: UICollectionView {
                override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
                    super.init(frame: frame, collectionViewLayout: layout)
                    commonInit()
                }
            
                required init?(coder aDecoder: NSCoder) {
                    super.init(coder: aDecoder)
                    commonInit()
                }
            
                private func commonInit() {
                    isScrollEnabled = false
                }
            
                override var contentSize: CGSize {
                    didSet {
                        invalidateIntrinsicContentSize()
                    }
                }
            
                override func reloadData() {
                    super.reloadData()
                    self.invalidateIntrinsicContentSize()
                }
            
                override var intrinsicContentSize: CGSize {
                    return contentSize
                }
            }
            

            您可能不需要覆盖reloadData

            【讨论】:

            • 这是在 iOS 14 上唯一对我有用的东西。谢谢!
            【解决方案13】:
            1. 子类 UICollectionView 如下
            2. 删除高度限制(如果有)
            3. 打开内在大小

            -

            class ContentSizedCollectionView: UICollectionView {
                override var contentSize:CGSize {
                    didSet {
                        invalidateIntrinsicContentSize()
                    }
                }
            
                override var intrinsicContentSize: CGSize {
                    layoutIfNeeded()            
                    return CGSize(width: UIView.noIntrinsicMetric, height: collectionViewLayout.collectionViewContentSize.height)
                }
            }
            

            【讨论】:

              【解决方案14】:

              如果你设置了集合视图的高度约束。只需观察 viewDidLoad 中 contentSize 的变化并更新约束即可。

                      self.contentSizeObservation = collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, change in
                          guard let `self` = self else { return }
                          guard self.collectionView.contentSize != .zero else { return }
                          self.collectionViewHeightLayoutConstraint.constant = self.collectionView.contentSize.height
                      }
              

              【讨论】:

                【解决方案15】:

                我有一个多行、多选的UICollectionView 子类,其中单元格的高度固定,左对齐,从左到右流动。它嵌入在垂直滚动视图内的垂直堆栈视图中。请参阅标签“Property Types”下方的 UI 组件。

                为了使集合视图适合其contentSize 的高度,这是我必须做的(注意这都在UICollectionView 子类中):

                1. 为集合视图提供优先级为999 的非零最小高度约束。将集合视图的大小自动调整为其内容高度根本不适用于零高度。

                  let minimumHeight = heightAnchor.constraint(greaterThanOrEqualToConstant: 1)
                  minimumHeight.priority = UILayoutPriority(999)
                  minimumHeight.isActive = true
                  
                2. 将集合视图的内容拥抱优先级设置为垂直轴.required

                  setContentHuggingPriority(.required, for: .vertical)
                  
                3. 调用reloadData() 之后是以下调用:

                  invalidateIntrinsicContentSize()
                  setNeedsLayout()
                  layoutIfNeeded()
                  

                  例如,我的子类中有一个setItems() 函数:

                  func setItems(_ items: [Item]) {
                    self.items = items
                    selectedIndices = []
                    reloadData()
                    invalidateIntrinsicContentSize()
                    setNeedsLayout()
                    layoutIfNeeded()
                  }
                  
                4. 如下覆盖contentSizeintrinsicContentSize

                  override var intrinsicContentSize: CGSize {
                    return contentSize
                  }
                  
                  override var contentSize: CGSize {
                    didSet {
                      invalidateIntrinsicContentSize()
                      setNeedsLayout()
                      layoutIfNeeded()
                    }
                  }
                  

                【讨论】:

                • 你能分享完整的代码吗?
                【解决方案16】:

                将 UICollectionView 的高度调整为它的内容大小的高度 ??

                SWIFT 5

                final class MyViewController: UIViewController {
                
                    // it's important to declare layout as separate constant due to late update in viewDidLayoutSubviews()
                    private let layout = UICollectionViewFlowLayout()
                    private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
                
                
                    override func viewDidLoad() {
                        super.viewDidLoad()
                    
                        setupCollectionView()
                        setupCollectionViewConstraints()
                    }
                
                    override func viewDidLayoutSubviews() {
                        super.viewDidLayoutSubviews()
                    
                        updateFlowLayout()
                    }
                
                    private func setupCollectionView() {
                        view.addSubview(collectionView)
                        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "UICollectionViewCell")
                        collectionView.dataSource = self
                    }
                
                    private func setupCollectionViewConstraints() {
                        // your collectionView constraints setup
                    }
                
                    private func updateFlowLayout() {
                        let height = collectionView.collectionViewLayout.collectionViewContentSize.height
                        layout.itemSize = CGSize(width: view.frame.width, height: height)
                        layout.scrollDirection = .horizontal
                        layout.minimumInteritemSpacing = .zero
                        layout.minimumLineSpacing = .zero
                        layout.sectionInset = UIEdgeInsets.zero
                    }
                }
                
                extension MyViewController: UICollectionViewDataSource {
                     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {...}
                     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {...}
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-02-22
                  • 2013-06-05
                  • 2022-01-22
                  • 1970-01-01
                  • 2014-01-14
                  • 1970-01-01
                  • 2017-03-13
                  • 1970-01-01
                  相关资源
                  最近更新 更多