[翻译]在objective c建立自定义collection view样式

建立自定义collection样式

苹果在ios6中新增了一个更加易于建立和管理复杂用户界面的类:collection view。在此以前ios6上面用于展现多项列表的是table view,虽然名称是表格但它展现信息的形式并非表格形式,而是垂直列表,固然在实际设计展现垂直可滚动的文本型列表时很是有用,可是你会遇到许多垂直可滚动的列表所存在的局限性,好比不能水平滚动,每行只有一项单元格,没有复杂的样式,在ios6以前你想要作到这些事情,只能靠自定义扩展。 在ios6 平台介绍了一个新的类:collection view ,它拥有全部table view的功能,新增了许多展现信息的方式而且变得更加易用。 table view 的功能很是有限,而collection view 则能够进行更加灵活的设计。它能够按不一样的形式展现表格内容:横向或垂直滚动,列表或表格或任何你想要的自定义样式,它还提供了很是灵活的样式定义去为每个 collection view 设定不一样的展示形式,苹果提供了一个简单的例子来讲明这个样式功能将会知足许多人的须要,这个 flow layout 是基于每一行定义的布局,每个单元均可以线性地展示在上一个单元后面,直到该行填充完以后,切换到下一行。这将会根据他的配置项来决定时垂直仍是水平滚动,或者网格形式(每行有多个单元格)。html

即便你的样式不那么简单,你仍然可使用collection view。只须要提供自定义样式便可。本文将会提供一个建立自定义样式的例子,我不打算提供一个完整的collection view 例子或者样式, 苹果的手册和WWDC视频(205-219)已经提供了很是好的样例。本文将会基于一个已完成的例子进行自定义样式调整。ios

如今让咱们来看看咱们想要自定义的样式:一些单元格很大,而其余的会小一些。 img 要支持这个样式,咱们须要继承并重写 UICollectionViewLayout 告诉collection 要如何去展示单元格。缓存

首先咱们来定义collection view 的大小。咱们的样式将会展现12个单元格在一页,假如多于12个单元格,咱们将会在按水平方向滚动展现。因此计算一页会有多少单元格展现是一件很是简单的事情,咱们只须要根据collection view 的数据源(data source)提供的数据进行计算就能够知道咱们总共须要展现多少页。我把这个 collectionVIewCOntenSize 方法重写了:性能优化

`- (CGSize)collectionViewContentSize { // Ask the data source how many items there are (assume a single section) id<UICollectionViewDataSource> dataSource = self.collectionView.dataSource; int numberOfItems = [dataSource collectionView:self.collectionView numberOfItemsInSection:0];app

// Determine how many pages are needed
int numberOfPages = ceil((float)numberOfItems / (float)kNumberOfItemsPerPage);

// Set the size
float pageWidth = self.collectionView.frame.size.width;
float pageHeight = self.collectionView.frame.size.height;
CGSize contentSize = CGSizeMake(numberOfPages * pageWidth, pageHeight);

return contentSize;

}`框架

下一步咱们要自定义每一页中须要展现的单元格样式,对于每个单元格,咱们的样式要处理的事情:布局

  • 这个单元格在第几页
  • 这个单元格在第几行(或者它的垂直偏移量是多少)
  • 这个单元格在该行第几位(或者它的水平偏移量是多少)
  • 它的大小

collection view 有两种方式获取样式的信息,当你须要在一个新的区域展现collection的时候,它将会向-layoutAttributesForElementsInRect:传递一个 rect 定义以后注册一个collection view。它也能够按照相对的布局信息,经过调用-layoutAttributesForItemAtIndexPath: 来返回每个单元格须要样式信息。对于布局来讲,咱们也可使用惰性计算而没必要计算每个元素的布局信息,例如:咱们的应用会有大约100个单元格的布局,为了简单起见,我老是预先计算好布局的属性而且缓存起来,这样的处理最终被证实是一个很不错的性能优化方式。性能

根据苹果提供的文档,样式将会在调用这几个方法的时候被初始化:优化

  1. prepareLayout
  2. collectionViewContentSize
  3. layoutAttributesForElementsInRect

因此咱们将会在初始化 -prepareLayout 方法的时候计算全部的样式信息。有很是多的方式能够计算每个样式信息,可是在这里我将会使用一个简单的例子:this

由于我知道每一页给定的单元格式12,因此我决定根据按页给予每个元素定义不一样的参数,我能够设置每一页的展现单元格为水平方式,当存在多个页面时,将会按照页面宽度去初始化collection view的宽度。这意味着我要在得到元素以后计算出正确的页数,这里咱们知道每一页须要展现12个单元格,我能够将总数除以12来得到正确的页数。而后根据每个单元格的水平和垂直偏移量来计算它的位置,这里能够看出来水平方向只有4个单元格,而其中三个比较小,因此咱们可使用很简单的switch语句来肯定每个单元格的样式参数,而后就能够根据这些值为每个单元格建立一个布局属性对象。

`

  • (CustomFlowLayoutAttributes *)layoutAttributesForItemAtIndex:(int)index { NSIndexPath *path = [NSIndexPath indexPathForItem:index inSection:0]; CustomFlowLayoutAttributes *attributes = (CustomFlowLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:path];

    // Figure out what page this item is on int pageNumber = floor((float)index / (float)kNumberOfItemsPerPage);

    // Set the horizontal offset for the start of the page float pageWidth = self.collectionView.frame.size.width; float horizontalOffset = pageNumber * pageWidth;

    // Now, determine which position this cell occupies on the page.
    int indexOnPage = index % kNumberOfItemsPerPage;

    int column = 0; switch (indexOnPage) { case 0: case 3: case 5: column = 0; break; case 1: case 4: case 6: case 9: column = 1; break; case 2: case 7: case 10: column = 2; break; case 8: case 11: column = 3; break; default: column = 0; break; }

    int row = 0; switch (indexOnPage) { case 0: case 1: case 2: row = 0; break; case 3: case 4: row = 1; break; case 5: case 6: case 7: case 8: row = 2; break; case 9: case 10: case 11: row = 3; break; default: row = 0; break; }

    horizontalOffset = horizontalOffset + ((kColumnWidth + kPadding) * column); float verticalOffset = (kRowHeight + kPadding) * row;

    // finally, determine the size of the cell. float width = 0.0; float height = 0.0;

    switch (indexOnPage) { case 2: width = kLargeCellWidth; height = kLargeCellHeight; break; case 5: width = kColumnWidth; height = kMediumCellHeight; break; default: width = kColumnWidth; height = kRowHeight; break; }

    CGRect frame = CGRectMake(horizontalOffset, verticalOffset, width, height); [attributes setFrame:frame];

    return attributes; }`

这样定义样式计算的属性值以后,咱们就能够很简单的经过向layoutAttributesForItemAtIndexPath: 或者 layoutAttributesForElementsInRect发送请求获取每个单元格的属性。

这就是为使用 collection view 所须要自定义的样式,它将会容许collection view 在指定单元格的位置显示正确数目的单元格。这里只须要咱们作一个简单的事情:继承并显示咱们的单元格。就会得到三个不一样大小的单元格框架(一些小的,一个很大而且占用两行的单元格和一个中等大小的)这三种不一样的单元格就像咱们代码所设定的同样显示。最后咱们在每个单元格类中定义单元格的内容,咱们就能够获得自定义样式并将这些样式信息添加到每个单元格属性中。

要作到这个咱们只须要: 继承 UICollectionViewLayoutAttributes 去建立一个自定义的子类并将咱们想要扩展的信息添加到子类中传递给咱们的单元格(在这里咱们用 cell 类型)。注意建立的时候要添加一个property, 我须要重写一个父类接口的 copyWithZone: 来保证添加的 property 可以使用NSCopying 协议去建立一个 UICollectionView的副本传递给单元格。

实现这个UICollectionViewLayoutAttriButes的方法,咱们自定义的样式子类将会返回咱们想要的样式属性。

`

  • (Class)layoutAttributesClass { return [CustomFlowLayoutAttributes class]; }`

而后我只须要在我实现UICollectionVIewCell子类中计算附加的样式属性值和实现一些逻辑,就能够在接收这些布局参数以后,获得一个我须要的布局。

虽然苹果的文档(以及无数次提到的WWDC视频)已经很是描述得很是清楚。可是对大多数人来讲,只要使用标准的流式布局就应该足够了,若是你发如今流式布局的状况下不能彻底知足你的需求的时候,也许你须要建立一个自定义的布局,这只须要占用你一点点的工做时间就能够实现。

原帖来自:http://stripysock.com.au/blog/2013/2/21/creating-a-custom-collection-view-layout

相关文章
相关标签/搜索