UICollectionView使用UICollectionViewFlowLayout

时间:2016-04-27 02:30:05

标签: ios objective-c swift uikit uicollectionviewlayout

我有一个分为多个部分的数据集,但是,我想在collectionView中显示它,而不会在各部分之间中断。以下是我想要实现的目标:

Instead of:
0-0 0-1 0-2
0-3
1-0 1-1
2-0
3-0

I want:
0-0 0-1 0-2
0-3 1-0 1-1
2-0 3-0

我意识到解决方案可能在于自定义UICollectionViewLayout子类,但我不确定如何实现这样的目标。

由于

2 个答案:

答案 0 :(得分:2)

你是正确的,你需要继承UICollectionViewLayout。 在开始之前要理解的本质是,您需要至少计算集合视图中每个单元格的位置和大小。 UICollectionViewLayout只是一种提供该信息的结构化方式。你得到了结构,但你必须自己提供其他一切。

您需要覆盖4种方法:

  • 制备
  • 的invalidateLayout
  • layoutAttributesForItemAtIndexPath
  • layoutAttributesForElementsInRect

一个技巧是将布局属性缓存在查找表(字典)中:

var cachedItemAttributes = [IndexPath: UICollectionViewLayoutAttributes]()

准备中,您可以计算collectionView中每个indexPath的布局属性:

override func prepare() {
    super.prepare()
    calculateAttributes()
}

invalidateLayout 中,重置缓存的布局属性并重新计算它们:

override func invalidateLayout() {
    super.invalidateLayout()
    cachedItemAttributes = [:]
    calculateAttributes()
} 

layoutAttributesForItemAtIndexPath 中,您可以使用查找表返回正确的布局属性:

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return cachedItemAttributes[indexPath]
}

layoutAttributesForElementsInRect 中,您可以过滤查找表中指定矩形内的元素:

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    return cachedItemAttributes.filter { rect.intersects($0.value.frame) }.map { $0.value }
}

拼图的最后一部分是布局属性的实际计算。这里我只提供伪代码:

func calculateAttributes() {
    // For each indexpath (you can get this from the collectionView property using numberOfSections and numberOfItems:inSection )
    // calculate the frame, i.e the origin point and size of each cell in your collectionView and set it with UICollectionViewLayoutAttributes.frame
    // There are many other properties on UICollectionViewLayoutAttributes that you can tweak for your layout, but frame is a good starting point from which you can start experimenting.
    // Add the layout attributes to the lookup table
    // end loop
}

要回答您的问题,这里有伪代码来计算每个单元格的位置:

// If width of cell + current width of row + spacing, insets and margins exceeds the available width
// move to next row.
// else
// cell origin.x = current width of row + interitem spacing
// cell origin.y = number of rows * (row height + spacing)
// endif

如果您需要自定义布局是可配置的,那么如果可用签名足够,则使用UICollectionViewDelegateFlowLayout,或者定义从UICollectionViewDelegateFlowLayout或UICollectionViewDelegate继承的自己的布局。因为您的协议继承自UICollectionViewDelegateFlowLayout,它本身继承自UICollectionViewDelegate,所以您可以将其直接设置为viewcontroller中的collectionView委托。在自定义集合视图布局中,您只需将委托从UICollectionViewDelegate强制转换为自定义协议即可使用它。请记住处理转换失败或委托未实现协议方法的情况。

答案 1 :(得分:2)

我发现对于我来说,Marmoy的答案缺少另外一个要素:

覆盖collectionViewContentSize。

否则,根据collectionView的大小,您可能会调用一个layoutAttributesForElements(在rect:CGRect中),其宽度或高度为零,这会丢失许多单元格。如果要在集合视图中动态调整项目大小,则尤其如此。

因此,Marmoy的答案的更完整版本为:

    array([[-0.44547156, -0.45989361, -0.54462862, -0.77093382, -0.42889501,
    -0.46236286, -0.58319129, -0.56385354, -0.58254861, -0.7476285 ],
   [-0.09561464,  0.00522743,  0.01977207, -0.71317843, -0.73268128,
     0.01925189, -0.04707032, -0.44227692, -0.06843101, -0.80253451],
   [-0.13276529, -0.22165378,  0.08195758, -0.06191557, -0.22300162,
     0.09476953, -0.71540623,  0.35323137, -0.26945605, -0.006015  ],
   [ 0.36005916,  0.66788728,  0.21763805,  0.54523638,  0.13773763,
    -0.04109221,  0.33481649,  0.02811258, -0.01340615,  0.52465671]])

此外,对于您的委托人来说,在上面的示例中实现UICollectionViewDelegateFlowLayout也很重要...或者,如果您知道布局中的项目大小而又不知道单元格内容,则可以只计算布局中的项目大小。