使用动态大小时 uicollectionView 单元格时如何设置特定单元格的大小

How to set Size for a specific cell when using dynamic size uicollectionView cell

我正在学习本教程:

https://www.raywenderlich.com/4829472-uicollectionview-custom-layout-tutorial-pinterest

实现这个布局

基本上 section 1 中的水平 collectionViewsection 2

中的垂直 collectionView

我的想法是在第一个单元格中放置一个 collectionView,其大小应为:CGSize(width: screen.width, height: 100)

但是由于我使用的是 link 中的方法,所以我不知道如何为第一个单元格指定特定大小。这是我试过的:

为了能够控制集合项的大小,您可以遵循的一种策略是让 UICollection 视图的委托派生自 UICollectionViewDelegateFlowLayout 而不是仅派生自 UICollectionViewDelegate.通过从 UICollectionViewDelegateFlowLayout 派生,您的委托可以实现:

func collectionView(UICollectionView, layout: UICollectionViewLayout, sizeForItemAt: IndexPath) -> CGSize

并告诉系统第一项的大小。

免责声明: 由于 OP 之前曾询问 question 如何创建 Pinterest 布局,并且我在评论中建议了 Raywenderlich 教程,我相信这个问题来自 OP是

的扩展

答案:

查看您添加的设计,我相信您正在尝试添加带有水平滚动条的 CollectionView 作为跨越整个 collection 视图宽度的第一个单元格。因为您已经对 UICollectionViewFlowLayout 进行了子类化,所以您需要在 prepare 方法本身

中处理这种特殊情况
    override func prepare() {
        // 1
        guard
            cache.isEmpty,
            let collectionView = collectionView
        else {
            return
        }
        // 2
        let columnWidth = contentWidth / CGFloat(numberOfColumns)
        var xOffset: [CGFloat] = []
        for column in 0..<numberOfColumns {
            xOffset.append(CGFloat(column) * columnWidth)
        }
        var column = 0
        var yOffset: [CGFloat] = .init(repeating: 0, count: numberOfColumns)

        // 3
        for item in 0..<collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            let attributes: UICollectionViewLayoutAttributes
            let photoHeight = delegate?.collectionView(
                collectionView,
                heightForPhotoAtIndexPath: indexPath) ?? 180
            let height = cellPadding * 2 + photoHeight
            let frame: CGRect

            if item == 0 {
                frame = CGRect(x: 0,
                               y: yOffset[column],
                               width: columnWidth,
                               height: height)

                contentHeight = frame.maxY
                for column in 0..<numberOfColumns {
                    yOffset[column] = frame.maxY
                }
                column = 0
            }
            else {
                // 4
                frame = CGRect(x: xOffset[column],
                               y: yOffset[column],
                               width: columnWidth,
                               height: height)

                contentHeight = max(contentHeight, frame.maxY)
                yOffset[column] = yOffset[column] + height
                column = column < (numberOfColumns - 1) ? (column + 1) : 0
            }

            let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
            attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = insetFrame
            cache.append(attributes)
        }
    }

在您的 for item in 0..<collectionView.numberOfItems(inSection: 0) { 中,我添加了一个处理 if item == 0 { 的特殊条件,其中我没有使用列宽,而是使用了内容宽度(根据您的设计,您希望第一个单元格覆盖collection 视图的整个宽度)

终于在您的委托方法中

extension PhotoStreamViewController: PinterestLayoutDelegate {
  func collectionView(
      _ collectionView: UICollectionView,
      heightForPhotoAtIndexPath indexPath:IndexPath) -> CGFloat {
          if indexPath.row == 0 {
              return 180 // whatever is the height of first cell
          }
          return photos[indexPath.item].image.size.height
      }
}

忠告

在您的设计中,我可以看到您添加了两个部分 headers Header 1Header 2 我想在那种情况下,您返回的部分数量至少为 2,但本教程侧重于单个部分 collection 视图,如果您注意到

for item in 0..<collectionView.numberOfItems(inSection: 0) {

第 0 部分是硬编码的,如果您想处理多个部分并且可能需要围绕 prepare

进行大量修改,您可能必须动态处理它