为什么在将 UICollectionViewLayout 分配给 UICollectionView 后,UICollectionViewCells 不再可见?

Why are UICollectionViewCells no longer visibile after assigning a UICollectionViewLayout to the UICollectionView?

我的应用有一个 UIViewController class;在这个 class 中,我连接了一个从 Storyboard 加载的 UICollectionView

我正在使用 UICollectionViewLayout class 创建自定义布局。这是它的样子:

class MyLayout: UICollectionViewLayout {

    override func prepareLayout() {
        super.prepareLayout()
    }

    override func collectionViewContentSize() -> CGSize {
        let attributes = super.collectionViewContentSize()
        return attributes
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
        let attributes = super.layoutAttributesForElementsInRect(rect) as? [UICollectionViewLayoutAttributes]
        return attributes
    }

    override func layoutAttributesForItemAtIndexPath(indexPath:
    NSIndexPath) -> UICollectionViewLayoutAttributes {
        let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
        return attributes
    }
}

要将 UICollectionViewLayout 分配给 UICollectionView,我使用 UICollectionViewcollectionViewLayout 属性:

myCollectionView.collectionViewLayout = MyLayout()

运行 应用程序,UICollectionViewCells 不再可见。不过,它们在分配 UICollectionViewLayout 之前是可见的。我现在只能看到 UICollectionView.

的背景

为什么单元格不再可见?

更新

我仔细查看了 UICollectionViewUICollectionViewLayoutAttributes,尤其是 contentSize。我把它的值打印出来,好像等于(0.0, 0.0)layoutAttributesForElementsInRectattributes 值也等于 nil。绝对是一个危险信号。

Download the project

我认为你可以让几乎所有的东西都完全一样。将自定义布局的 class 类型更改为 UICollectionViewFlowLayout

class myLayout: UICollectionViewFlowLayout {
    //all your code here
}

此外,将 myLayout 更改为 MyLayout 以取得良好效果:)

首先,您应该使用布局初始化 UICollectionView,而不是事后设置它:

var collectionView = UICollectionView(frame: frame, collectionViewLayout: myLayout)

来自documentation:

You normally specify a layout object when creating a collection view [...]

下一步,如果你子class UICollectionViewLayout,你必须实现collectionViewContentSize和return一个你自己的值.在这里调用 super 是未定义的,除非你是 subclassing UICollectionViewFlowLayout

Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size for scrolling purposes.

那是因为 UICollectionViewLayout 本身是一个抽象class,什么都不做(它应该被子class编辑)。

The UICollectionViewLayout class is an abstract base class that you subclass and use to generate layout information for a collection view.

同理,你还需要在layoutAttributesForElementsInRect:中计算你自己的layoutAttributes。最简单的方法是在 prepareLayout 方法中计算所有你需要的布局,然后只得到你需要的那些。这是自定义布局的"core":

  • 向委托人询问您必须在 collectionView 中显示的元素数量。
  • 对于每个元素,首先创建正确的 indexPath,然后使用 UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath).
  • 为该 indexPath 创建一个空的 layoutAttribute
  • 计算并设置该布局属性的 frame 属性。
  • 将该 layoutAttribute 存储在声明为 MutableArray 的私有 属性 中。
  • layoutAttributesForElementsInRect: 中,遍历您之前实例化的所有存储的 layoutAttributes,以及 return 其 frame 与提供的 rect 相交的子集。 UICollectionView 将要求其数据源为每个 layoutAttributes 提供一个单元格 return 通过该方法(通过 cellForItemAtIndexPath)编辑,以及那些 frame将使用这些布局属性中的 frame 属性 设置单元格。

如果你能阅读 Objective-C 和 Swift,你可以看看 my sample UICollectionViewLayout implementation here, which makes the UICollectionView mimic the daily view of the iOS calendar app (screenshot)。

如果您的目标是实现相当标准的布局(即放置在水平或垂直流动的网格中的元素),我建议您从 subclassing UICollectionViewFlowLayout 相反,因为它已经是 UICollectionViewLayout 的有效实现,这意味着您可以在大多数方法中使用 super 来获取默认值。

此外,关于如何创建您自己的自定义布局的好书 here in the official documentation