集合视图中的动态高度单元格

托尔盖·托克拉(Tolgay Toklar)

我想像这样的应用程序调整单元格的大小:

在此处输入图片说明

如您所见,右边的单元格比左边的单元格短。为了达到同样的目的,我使用了以下代码:

var testI=1
func collectionView(collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                           sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    var cellHeight=287
    if testI % 2 == 0 {
        cellHeight=247
    }
    testI += 1
    return CGSizeMake(CGFloat(176), CGFloat(cellHeight))
}

结果:

在此处输入图片说明

我不想要这些顶部空间。如何删除它们?我的收藏夹视图属性:

在此处输入图片说明

我该如何实现?

奥兹古尔

UICollectionViewFlowLayout希望所讨论的单元格具有相同的高度(或宽度,取决于滚动方向),以便它可以正确地水平或垂直对齐它们。

Thus, you can't use that to get the above effect. You need to create your own layout class which will have similar properties as UICollectionViewFlowLayout such as minimumInteritemSpacing, sectionInset etc. Besides, the new subclass (which I will name UserCollectionViewLayout from now on) will be capable of generating all the layout attributes for each corresponding cell with a frame value having different height so that cells will be positioned just like in the image you've shared.

Before we begin, you should at least have a basic understanding about how to create custom layouts so I strongly recommend you to read this article for a fresh start.

First, we need to create our own UICollectionViewLayoutAttributes class so we can store the height of the cell there for later use.

class UserCollectionViewLayoutAttributes: UICollectionViewLayoutAttributes {
  var height: CGFloat = 0.0
}

Layout classes sometimes copy UICollectionViewLayoutAttributes when needed, so we need to override several methods to be able to pass height to copied object when it happens:

override func copyWithZone(zone: NSZone) -> AnyObject {
  let copy = super.copyWithZone(zone) as! UserCollectionViewLayoutAttributes
  copy.height = height
  return copy
}

override func isEqual(object: AnyObject?) -> Bool {
  if let object = object as? UserCollectionViewLayoutAttributes {
    if height != object.height {
      return false
    }
    return super.isEqual(object)
  }
  return false
}

We are going to need a new protocol which will ask for delegate the height of each cell:

(Similar to UICollectionViewDelegateFlowLayout.sizeForItemAtIndexPath)

protocol UserCollectionViewLayoutDelegate: UICollectionViewDelegate {
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UserCollectionViewLayout, heightForItemAtIndexPath indexPath: NSIndexPath) -> CGFloat 
}

OK, everything we need on our way to defining the layout is there. So, let's start:

class UserCollectionViewLayout: UICollectionViewLayout {
  private var contentHeight: CGFloat = 0.0
  private var allAttributes = [UserCollectionViewLayoutAttributes]()

  weak var delegate: UserCollectionViewLayoutDelegate!

  var numberOfColumns: Int = 2

  private var numberOfItems: Int {
    return collectionView?.numberOfItemsInSection(0) ?? 0
  }

  private var contentWidth: CGFloat {
    return CGRectGetWidth(collectionView?.bounds ?? CGRectZero)
  }

  private var itemWidth: CGFloat {
    return round(contentWidth / CGFloat(numberOfColumns))
  }
  ...

OK, let's explain the above properties piece by piece:

  • contentHeight: Content height of collection view so it will know how to/where to scroll. Same as defining scrollView.contentSize.height.
  • allAttributes: An array holding the attributes of each cell. It will be populated in prepareLayout() method.
  • numberOfColumns: A value indicating how many cells will be shown at each row. It will be 2 in your example as you want to show 2 photos in a row.

Let's continue with overriding some important methods:

override func collectionViewContentSize() -> CGSize {
  return CGSize(width: contentWidth, height: contentHeight)
}

override class func layoutAttributesClass() -> AnyClass {
  return UserCollectionViewLayoutAttributes.self
}

The crucial part is to implement prepareLayout correctly. In this method, we are going to populate all the layout attributes corresponding to each indexPath, and store them in allAttributes for later use.

The layout looks like a 2D matrix of cells. However, height of each cell might be different. Thus, you should keep in mind that frame.origin.y value might not be same for cells positioned in the same row:

override func prepareLayout() {
  if allAttributes.isEmpty {
    let xOffsets = (0..<numberOfColumns).map { index -> CGFloat in
      return CGFloat(index) * itemWidth
    }
    var yOffsets = Array(count: numberOfColumns, repeatedValue: CGFloat(0))
    var column = 0

    for item in 0..<numberOfItems {
      let indexPath = NSIndexPath(forItem: item, inSection: 0)
      let attributes = UserCollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)

      let cellHeight = delegate.collectionView(collectionView!, layout: self, heightForItemAtIndexPath: indexPath)

      let frame = CGRect(x: xOffsets[column], y: yOffsets[column], width: itemWidth, height: cellHeight)

      attributes.frame = frame
      attributes.height = cellHeight

      allAttributes.append(attributes)

      yOffsets[column] = yOffsets[column] + cellHeight

      column = (column >= numberOfColumns - 1) ? 0 : column + 1
      contentHeight = max(contentHeight, CGRectGetMaxY(frame))
    }
  }
}

override func invalidateLayout() {
  allAttributes.removeAll()
  contentHeight = 0
  super.invalidateLayout()
}   

We need to implement 2 more methods to let the layout know which layout attribute it should use upon displaying a cell which are:

Let's give it a shot:

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
  return cachedAttributes.filter { attribute -> Bool in
    return CGRectIntersectsRect(rect, attribute.frame)
  }
}

override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
  return cachedAttributes.filter { attribute -> Bool in
    return attribute.indexPath == indexPath
  }.first
}

OK, that's all for the layout part. Right now, we have a layout class capable of displaying cells with different height values in a single row. Now, we need to figure out how to pass height value to actual cell object.

Luckily for us, it is really straightforward if you are organizing your cell content using AutoLayout. There is a method named applyLayoutAttributes in UICollectionViewCell which enables you to make last minute changes on cell's layout.

class UserCell: UICollectionViewCell {

  let userView = UIView(frame: CGRectZero)
  private var heightLayoutConstraint: NSLayoutConstraint!

  override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes) {
    super.applyLayoutAttributes(layoutAttributes)
    let attributes = layoutAttributes as! UserCollectionViewLayoutAttributes
    heightLayoutConstraint.constant = attributes.height
  }

  override init(frame: CGRect) {
    super.init(frame: frame)
    contentView.backgroundColor = UIColor.whiteColor()
    contentView.addSubview(userView)
    userView.translatesAutoresizingMaskIntoConstraints = false

    contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
      "H:|[userView]|",
      options: NSLayoutFormatOptions.AlignAllCenterY,
      metrics: nil,
      views: ["userView": userView])
    )

    contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
      "V:|[userView]|",
      options: NSLayoutFormatOptions.AlignAllCenterX,
      metrics: nil,
      views: ["userView": userView])
    )

    heightLayoutConstraint = NSLayoutConstraint(
      item: self, 
      attribute: NSLayoutAttribute.Height, 
      relatedBy: NSLayoutRelation.Equal, 
      toItem: nil, 
      attribute: NSLayoutAttribute.NotAnAttribute, 
      multiplier: 0.0, 
      constant: 0.0
    )
    heightLayoutConstraint.priority = 500
    contentView.addConstraint(heightLayoutConstraint)
  }
}

Getting all of them together, here is the result:

在此处输入图片说明

I am sure you can add minimumInteritemSpacing and sectionInset properties just like in UICollectionViewFlowLayout class in order to add some padding between cells.

(Hint: you should tweak cell's frame in prepareLayout method accordingly.)

You can download the example project from here.

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

CollectionView动态单元格高度快速

来自分类Dev

UICollectionView-动态单元格高度?

来自分类Dev

表格视图中动态单元格高度中的标签未正确显示文本

来自分类Dev

如何设置集合视图的动态高度,而不是“视图”的“单元格”?

来自分类Dev

集合视图单元格不在图像视图中显示图像

来自分类Dev

呈现弹出视图时,如何让用户在父集合视图中选择单元格?

来自分类Dev

动态更改TableView单元格的高度

来自分类Dev

集合视图更改单元格大小

来自分类Dev

如何在集合视图中单选单元格?

来自分类Dev

集合视图中的动态高度单元格

来自分类Dev

如何快速设置集合视图单元格的动态高度?

来自分类Dev

集合视图中单元格的宽度不正确

来自分类Dev

在动态表格视图中添加静态单元格

来自分类Dev

动态单元格的高度

来自分类Dev

集合视图单元格内容分层

来自分类Dev

表格视图的动态单元格高度

来自分类Dev

如何在表视图中创建动态单元格?

来自分类Dev

集合视图单元格高度恢复为默认大小

来自分类Dev

集合视图更改单元格大小

来自分类Dev

如何使集合视图中的不同单元格进入Swift中的不同视图控制器

来自分类Dev

如何反转集合视图单元格

来自分类Dev

设置集合视图单元格的动态宽度和高度

来自分类Dev

动态计算collectionView单元格高度

来自分类Dev

如何通过快速点击集合视图的单元格在表视图中重新加载数据

来自分类Dev

表格视图中单元格的动态高度不起作用

来自分类Dev

为什么在 swift 3 中滚动后,集合视图中的动态集合单元格不显示为给定的大小和更改

来自分类Dev

确定集合视图单元格的大小

来自分类Dev

集合视图中每个单元格的 NSFetchedResultsController

来自分类Dev

从集合视图中删除未选择的单元格

Related 相关文章

  1. 1

    CollectionView动态单元格高度快速

  2. 2

    UICollectionView-动态单元格高度?

  3. 3

    表格视图中动态单元格高度中的标签未正确显示文本

  4. 4

    如何设置集合视图的动态高度,而不是“视图”的“单元格”?

  5. 5

    集合视图单元格不在图像视图中显示图像

  6. 6

    呈现弹出视图时,如何让用户在父集合视图中选择单元格?

  7. 7

    动态更改TableView单元格的高度

  8. 8

    集合视图更改单元格大小

  9. 9

    如何在集合视图中单选单元格?

  10. 10

    集合视图中的动态高度单元格

  11. 11

    如何快速设置集合视图单元格的动态高度?

  12. 12

    集合视图中单元格的宽度不正确

  13. 13

    在动态表格视图中添加静态单元格

  14. 14

    动态单元格的高度

  15. 15

    集合视图单元格内容分层

  16. 16

    表格视图的动态单元格高度

  17. 17

    如何在表视图中创建动态单元格?

  18. 18

    集合视图单元格高度恢复为默认大小

  19. 19

    集合视图更改单元格大小

  20. 20

    如何使集合视图中的不同单元格进入Swift中的不同视图控制器

  21. 21

    如何反转集合视图单元格

  22. 22

    设置集合视图单元格的动态宽度和高度

  23. 23

    动态计算collectionView单元格高度

  24. 24

    如何通过快速点击集合视图的单元格在表视图中重新加载数据

  25. 25

    表格视图中单元格的动态高度不起作用

  26. 26

    为什么在 swift 3 中滚动后,集合视图中的动态集合单元格不显示为给定的大小和更改

  27. 27

    确定集合视图单元格的大小

  28. 28

    集合视图中每个单元格的 NSFetchedResultsController

  29. 29

    从集合视图中删除未选择的单元格

热门标签

归档