【问题标题】:Calendar-like UICollectionView - how to add left inset before first item only?类似日历的 UICollectionView - 如何仅在第一项之前添加左插图?
【发布时间】:2021-09-15 04:20:56
【问题描述】:

我有以下UICollectionView: 它具有vertical 滚动、1 section31 items。 它具有基本设置,我正在计算 itemSize 以完全适合 7 per row。 目前它看起来像这样:

但是,我想在第一个项目之前插入一个,这样布局是均匀的,并且第一行和最后一行的项目数量相同。这是静态的,将始终包含 31 个项目,所以我基本上是尝试在第一个项目之前添加左边的空格/插入,所以它看起来像这样:

我尝试过使用自定义的UICollectionViewDelegateFlowLayout 方法:

collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int)

但由于只有 1 个部分有 31 行,因此它会插入所有行,而不仅仅是第一行。 我知道我可能可以再添加两个“空白”项目,但我觉得有一个更好的解决方案我可能不知道。有什么想法吗?

编辑:我试过 Tarun 的答案,但这不起作用。第一项的起源发生了变化,但其余的保持原样,因此首先与第二项重叠,其余的保持原样。将它们全部转移也不起作用。我最终得到:

【问题讨论】:

  • 你总是有 31 件商品吗?
  • 是的,@mahan,永远如此。但我想避免添加 2 个空行。

标签: swift uicollectionview uicollectionviewlayout


【解决方案1】:

您需要继承UICollectionViewFlowLayout,这样您就有机会为collectionView 中的所有项目自定义框架。

import UIKit

class LeftAlignCellCollectionFlowLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }
        guard let collectionView = self.collectionView else { return nil }
        
        var newAttributes: [UICollectionViewLayoutAttributes] = []
        let leftMargin = self.sectionInset.left

        let layout = collectionView.collectionViewLayout
        
        for attribute in attributes {
            if let cellAttribute = layout.layoutAttributesForItem(at: attribute.indexPath) {
                // Check for `indexPath.item == 0` & do what you want
                // cellAttribute.frame.origin.x = 0 // 80
                
                newAttributes.append(cellAttribute)
            }
        }
        
        return newAttributes
    }
}

现在您可以使用这个自定义布局类作为您的流布局,如下所示。

let flowLayout = LeftAlignCellCollectionFlowLayout()
collectionView.collectionViewLayout = flowLayout

【讨论】:

  • 这似乎不起作用。第一项的起源发生了变化,但其余的保持原样,因此首先与第二项重叠,其余的保持原样。将它们全部转移也不起作用。我已经编辑了我的答案,结果你的代码为我生成了。有什么想法吗?
  • 一旦你继承了流布局,你就负责整个布局。您不应该期望混合匹配(仅选择第一个项目框架自定义子类,其余部分使用默认值)。您还必须在子类中执行默认设置。这就像向超级实现询问 indexPath 的属性一样简单,移动它的 x 值,如果它水平超出范围,移动到下一行(也移动 y)。可以将其想象为您正在布置每个单元格/项目,而不是仅针对第一个单元格进行更改。
【解决方案2】:

按照 Taran 的建议,我决定使用自定义 UICollectionViewFlowLayout。这是一个通用答案,适用于 collectionView 中的任意数量的项目,以及任何 inset 值:

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    guard let collectionView = self.collectionView else { return nil }
    guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }

    var newAttributes: [UICollectionViewLayoutAttributes] = []

    for attribute in attributes {
        if let cellAttribute = collectionView.collectionViewLayout.layoutAttributesForItem(at: attribute.indexPath) {
            let itemSize = cellAttribute.frame.size.width + self.minimumInteritemSpacing
            let targetOriginX = cellAttribute.frame.origin.x + CGFloat(self.itemInset) * itemSize

            if targetOriginX <= collectionView.bounds.size.width {
                cellAttribute.frame.origin.x = targetOriginX
            } else {
                let shiftedPosition = lround(Double((targetOriginX / itemSize).truncatingRemainder(dividingBy: CGFloat(self.numberOfColumns))))

                cellAttribute.frame.origin.x = itemSize * CGFloat(shiftedPosition)
                cellAttribute.frame.origin.y += itemSize
            }

            newAttributes.append(cellAttribute)
        }
    }

    return newAttributes
}

地点:

  • self.itemInset 是我们想要从左边插入的值(我最初的问题是 2,但它可以是从 0number of columns-1 的任何数字)
  • self.numberOfColumns 顾名思义是 collectionView 中的列数。在我的示例中,这与天数有关,并且始终等于 7,但人们可能希望这是其他用例的通用值。

为了完整起见,我提供了一种方法,可以根据列数(天数)计算我的 callendar 集合视图的大小:

private func collectionViewItemSize() -> CGSize {
    let dimension = self.collectionView.frame.size.width / CGFloat(Constants.numberOfDaysInWeek) - Constants.minimumInteritemSpacing
    return CGSize(width: dimension, height: dimension)
}

对我来说,Constants.numberOfDaysInWeek 自然是 7Constants.minimumInteritemSpacing 等于 2,但它们可以是任何你想要的数字。

【讨论】:

  • @DuncanC 我也不确定,但我仍然希望其他人能从中受益,即使有人出于某种原因不喜欢它:) 谢谢!
  • @Fengson 我的回答只是一个建议,以指导您为您的用例使用正确的 API。很高兴您真正关心基于此发布一个可行的解决方案,而不是自己保留它。点赞!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-10
  • 1970-01-01
  • 2019-11-09
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
相关资源
最近更新 更多