【问题标题】:UICollectionView autosize heightUICollectionView 自动调整高度
【发布时间】:2014-01-14 13:12:13
【问题描述】:

如何正确调整 UICollectionView 的大小以使其完全显示其内容?我尝试了很多东西,包括设置它的框架、调用reloadData 和使布局无效:

self.collectionView.contentSize = CGSizeMake(300, 2000);
self.collectionView.frame = CGRectMake(0, 0, 300, 2000);
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];

但是这些都没有任何效果。按下按钮后,我仍然看到初始视图,如下所示:

我有一个小型演示程序,其中有一个生成 100 个元素的数据源。在 Interface Builder 中,我最初将 UICollectionView 的大小设置为一个较小的值,以便并非所有元素都适合,然后我按下一个按钮,然后执行上面的代码。我希望 UICollectionView 现在可以显示所有元素,但事实并非如此。

编辑:演示程序可以在https://github.com/mjdemilliano/TestUICollectionView找到。

EDIT2: 我观察到帧更新在某些时候会丢失,因为如果我再次按下按钮,当前帧会恢复到旧值。在按钮事件处理程序中添加一些日志语句后,日志输出为:

before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}
before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}

我不明白为什么不保留框架变化,是什么在改变它。

在某些时候,我会将硬编码的值替换为从流布局中获得的值,但我想排除这种情况并让我的示例尽可能简单。

上下文:我最终想要做的是:我有一个带有各种控件(如标签和图像)的可滚动视图,以及一个带有动态内容的集合视图。我想滚动所有这些,而不仅仅是集合视图,因此我没有使用集合视图自己的滚动工具,它工作正常。

【问题讨论】:

  • 当你说你的代码没有效果时——你的意思是字面意思吗?设置contentSize 是设置可滚动区域的正确方法。如果你不能滚动,那么你就没有设置contentSize。您确定self.collectionView 在您拨打电话时不是nil 吗?也许你不小心在viewDidLoad 之前打电话?
  • @Tommy:我添加了一个指向完整测试项目的链接。代码在 viewDidLoad 之后运行,因为它在按下按钮时运行。按下按钮时没有任何反应,但日志语句确认值已更新。然而,我认为更改集合视图的contentSizeframe 没有任何效果。请注意,我已禁用集合视图本身的滚动,因为我希望集合视图本身完整显示,然后在此工作后将其包装在滚动视图中。
  • Martijn,你能给我一个你的测试项目的更新链接吗?指定的 github 链接不起作用,我想做一些非常像你在这里描述的事情。
  • @JohnMichaelZorko 哎呀......两周前我刚刚从 Github 上删除了它,当时我是在自发的清洁狂热中,不知道我从这个问题中提到了它。对不起!我已经从我认为是这个测试项目的本地克隆中重新创建了它,希望它可以帮助你!
  • 发布了类似问题的答案:stackoverflow.com/a/67055114/855680

标签: ios objective-c uicollectionview nslayoutconstraint


【解决方案1】:

我最终通过修复所有自动布局问题解决了这个问题,使用约束修复了集合视图的高度。然后,每当我知道内容已更改时,我都会使用值 collectionView.contentSize.height 更新约束的值:

self.verticalLayoutConstraint.constant = self.collectionView.contentSize.height;

然后集合视图的大小被适当地调整,它在整个滚动视图中表现得很好。我已经用我的更改更新了 GitHub 测试项目。

对我来说,通过手动更新约束而不是告诉 iOS:“使集合视图的框架高度尽可能大”对我来说并不合适,但这是我最好的到目前为止已经想出了。如果您有答案,请发布更好的答案。

【讨论】:

  • 这仅适用于您在集合视图上指定了固定宽度(通过约束)。如果使用动态宽度约束,您必须在 UICollectionView 知道它应该有多宽之后运行该行。
  • 不敢相信没有更简单的方法,但我已经采取了同样的方法。
  • 感谢您在 github 上的代码,但这是一个小错误。重新加载数据后,您应该设置 self.verticalLayoutConstraint.constant = self.collectionView.collectionViewLayout.collectionViewContentSize.height;而不是 self.verticalLayoutConstraint.constant = self.collectionView.contentSize.height;
  • “我觉得不合适”对我来说也一样。但它有效,干得好!
  • 如何确定集合视图何时完成重新加载?
【解决方案2】:

它似乎与自定义 UICollectionView 类配合得很好。

class AutoSizedCollectionView: UICollectionView {

    override var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

在界面生成器中设置您的自定义类:

这样您还可以在界面生成器中将集合视图的固有大小设置为“占位符”,以避免必须设置高度限制。

我希望这对其他人有帮助。

【讨论】:

  • 工作就像一个魅力! - 斯威夫特 5
  • @SteveB 我们是否需要对此处的集合视图进行任何限制
  • 只要将collectionview固定在侧面和顶部和底部,就不需要任何高度限制
  • 嗨。我的集合视图位于 stackView 内。这种方法在这种情况下不起作用。 contentSize.height 总是返回 0。
【解决方案3】:

这是我在 Swift 3 中的实现:

override func sizeThatFits(_ size: CGSize) -> CGSize {
    if (self.superview != nil) {
        self.superview?.layoutIfNeeded()
    }

    return collectionView.contentSize
}

【讨论】:

  • 谢谢!!我认为它应该返回collectionView.collectionViewLayout.collectionViewContentSize
【解决方案4】:
UICollectionViewFlowLayout *flowLayout;
flowLayout = [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:NO];
[flowLayout setItemSize:CGSizeMake(322.0, 148.0)];  //important to leave no white space between the images
[self.collectionView setCollectionViewLayout:flowLayout];

我发现情节提要中的自动布局并没有太大帮助。为您的 collectionView 正确设置 UICollectionViewFlowLayout 才是真正的帮助。如果你用 setItemSize 调整项目大小,你可能会得到你想要的结果。

【讨论】:

  • 对我没有任何影响
  • @Pushp,可以放到viewDidLoad()中
【解决方案5】:

我发现的最简单的方法是按原样覆盖sizeThatFits: 方法:

- (CGSize)sizeThatFits:(CGSize)size
{
    if( self.superview )
        [self.superview layoutIfNeeded]; // to force evaluate the real layout

    return self.collectionViewLayout.collectionViewContentSize;
}

【讨论】:

    【解决方案6】:

    这是一种通过 CollectionView 的固有大小绑定高度的方法。 我用它来正确调整 TableView 单元内的 CollectionView 大小(具有动态单元格高度)。而且效果很好。

    首先,将其添加到您的 UICollectionView 子类中:

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

    然后在重新加载数据后调用 layoutIfNeeded():

    reloadData()
    layoutIfNeeded()
    

    【讨论】:

    • 您介意在线发布完整代码吗?我尝试了您的方法,但在intrinsicContentSize 中返回contentSize 实际上会使表格单元格崩溃,只有collectionViewLayout.collectionViewContentSize 有效。但我无法让表格增加它的单元格高度以包含reloadData();layoutIfNeeded() 的整个集合视图
    【解决方案7】:
    1. 为 CollectionView 高度约束添加 IBOutlet --> Like @IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
    2. 在下面添加代码。

    【讨论】:

      【解决方案8】:

      你可以试试我自定义的AGCollectionView

      • 使用情节提要或以编程方式分配 collectionView 的高度约束。

      - 将此课程分配给您的UICollectionView

      class AGCollectionView: UICollectionView {
      
          fileprivate var heightConstraint: NSLayoutConstraint!
      
          override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
              super.init(frame: frame, collectionViewLayout: layout)
              self.associateConstraints()
          }
      
          required public init?(coder aDecoder: NSCoder) {
              super.init(coder: aDecoder)
              self.associateConstraints()
          }
      
          override open func layoutSubviews() {
              super.layoutSubviews()
      
              if self.heightConstraint != nil {
                  self.heightConstraint.constant = floor(self.contentSize.height)
              }
              else{
                  self.sizeToFit()
                  print("Set a heightConstraint set size to fit content")
              }
          }
      
          func associateConstraints() {
              // iterate through height constraints and identify
      
              for constraint: NSLayoutConstraint in constraints {
                  if constraint.firstAttribute == .height {
                      if constraint.relation == .equal {
                          heightConstraint = constraint
                      }
                  }
              }
          }
      }
      

      【讨论】:

        【解决方案9】:

        对我来说,我认为它更简单

        -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
        
        {
        //add following code line after adding cells, before Return
        ...........
        .........
        scrollView.contentSize = = collectionView.contentSize;
        
        //now scrollView size is equal to collectionView size. No matter how small or big it is.
        
        return cell;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-04
          • 1970-01-01
          • 2010-10-17
          • 2013-06-05
          • 2011-08-23
          • 2012-08-21
          相关资源
          最近更新 更多