前几篇博客从uicollectionview的基础应用到设置uicollectionviewflowlayout更加灵活的进行布局,但都限制在系统为我们准备好的布局框架中,还是有一些局限性,例如,如果我要进行瀑布流似的不定高布局,前面的方法就很难满足我们的需求了,如下:
这种布局无疑在app的应用中更加广泛,商品的展示,书架书目的展示,都会倾向于采用这样的布局方式,当然,通过自定义flowlayout,我们也很容易实现。
首先,我们新建一个文件继承于uicollectionviewflowlayout:
<a href="http://my.oschina.net/u/2340880/blog/522806#">?</a>
1
<code>@interface mylayout : uicollectionviewflowlayout</code>
为了演示的方面,这里我不错更多的封装,添加一个属性,直接让外界将item个数传递进来,我们把重心方法重写布局的方法上:
2
3
<code>@property(nonatomic,assign)</code><code>int</code> <code>itemcount;</code>
<code>@end</code>
前面说过,uicollectionviewflowlayout是一个专门用来管理collectionview布局的类,因此,collectionview在进行ui布局前,会通过这个类的对象获取相关的布局信息,flowlayout类将这些布局信息全部存放在了一个数组中,数组中是uicollectionviewlayoutattributes类,这个类是对item布局的具体设置,以后咱们在讨论这个类。总之,flowlayout类将每个item的位置等布局信息放在一个数组中,在collectionview布局时,会调用flowlayout类layoutattributesforelementsinrect:方法来获取这个布局配置数组。因此,我们需要重写这个方法,返回我们自定义的配置数组,另外,flowlayout类在进行布局之前,会调用preparelayout方法,所以我们可以重写这个方法,在里面对我们的自定义配置数据进行一些设置。
简单来说,自定义一个flowlayout布局类就是两个步骤:
1、设计好我们的布局配置数据 preparelayout方法中
2、返回我们的配置数组 layoutattributesforelementsinrect方法中
示例代码如下:
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<code>@implementation mylayout</code>
<code>{</code>
<code> </code><code>//这个数组就是我们自定义的布局配置数组</code>
<code> </code><code>nsmutablearray * _attributeattay;</code>
<code>}</code>
<code>//数组的相关设置在这个方法中</code>
<code>//布局前的准备会调用这个方法</code>
<code>-(</code><code>void</code><code>)preparelayout{</code>
<code> </code><code>_attributeattay = [[nsmutablearray alloc]init];</code>
<code> </code><code>[super preparelayout];</code>
<code> </code><code>//演示方便 我们设置为静态的2列</code>
<code> </code><code>//计算每一个item的宽度</code>
<code> </code><code>float</code> <code>width = ([uiscreen mainscreen].bounds.size.width-self.sectioninset.left-self.sectioninset.right-self.minimuminteritemspacing)/2;</code>
<code> </code><code>//定义数组保存每一列的高度</code>
<code> </code><code>//这个数组的主要作用是保存每一列的总高度,这样在布局时,我们可以始终将下一个item放在最短的列下面</code>
<code> </code><code>cgfloat colhight[2]={self.sectioninset.top,self.sectioninset.bottom};</code>
<code> </code><code>//itemcount是外界传进来的item的个数 遍历来设置每一个item的布局</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>i=0; i<_itemcount; i++) {</code>
<code> </code><code>//设置每个item的位置等相关属性</code>
<code> </code><code>nsindexpath *index = [nsindexpath indexpathforitem:i insection:0];</code>
<code> </code><code>//创建一个布局属性类,通过indexpath来创建</code>
<code> </code><code>uicollectionviewlayoutattributes * attris = [uicollectionviewlayoutattributes layoutattributesforcellwithindexpath:index];</code>
<code> </code><code>//随机一个高度 在40——190之间</code>
<code> </code><code>cgfloat hight = arc4random()%150+40;</code>
<code> </code><code>//哪一列高度小 则放到那一列下面</code>
<code> </code><code>//标记最短的列</code>
<code> </code><code>int</code> <code>width=0;</code>
<code> </code><code>if</code> <code>(colhight[0]<colhight[1]) {</code>
<code> </code><code>//将新的item高度加入到短的一列</code>
<code> </code><code>colhight[0] = colhight[0]+hight+self.minimumlinespacing;</code>
<code> </code><code>width=0;</code>
<code> </code><code>}</code><code>else</code><code>{</code>
<code> </code><code>colhight[1] = colhight[1]+hight+self.minimumlinespacing;</code>
<code> </code><code>width=1;</code>
<code> </code><code>}</code>
<code> </code>
<code> </code><code>//设置item的位置</code>
<code> </code><code>attris.frame = cgrectmake(self.sectioninset.left+(self.minimuminteritemspacing+width)*width, colhight[width]-hight-self.minimumlinespacing, width, hight);</code>
<code> </code><code>[_attributeattay addobject:attris];</code>
<code> </code><code>}</code>
<code> </code>
<code> </code><code>//设置itemsize来确保滑动范围的正确 这里是通过将所有的item高度平均化,计算出来的(以最高的列位标准)</code>
<code> </code><code>if</code> <code>(colhight[0]>colhight[1]) {</code>
<code> </code><code>self.itemsize = cgsizemake(width, (colhight[0]-self.sectioninset.top)*2/_itemcount-self.minimumlinespacing);</code>
<code> </code><code>}</code><code>else</code><code>{</code>
<code> </code><code>self.itemsize = cgsizemake(width, (colhight[1]-self.sectioninset.top)*2/_itemcount-self.minimumlinespacing);</code>
<code>//这个方法中返回我们的布局数组</code>
<code>-(nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect{</code>
<code> </code><code>return</code> <code>_attributeattay;</code>
自定义完成flowlayout后,我们在viewcontroller中进行使用:
<code>- (</code><code>void</code><code>)viewdidload {</code>
<code> </code><code>[super viewdidload];</code>
<code> </code><code>// do any additional setup after loading the view, typically from a nib.</code>
<code> </code><code>mylayout * layout = [[mylayout alloc]init];</code>
<code> </code><code>layout.scrolldirection = uicollectionviewscrolldirectionvertical;</code>
<code> </code><code>layout.itemcount=100;</code>
<code> </code><code>uicollectionview * collect = [[uicollectionview alloc]initwithframe:cgrectmake(0, 0, 320, 400) collectionviewlayout:layout];</code>
<code> </code><code>collect.delegate=self;</code>
<code> </code><code>collect.datasource=self;</code>
<code> </code><code>[collect registerclass:[uicollectionviewcell </code><code>class</code><code>] forcellwithreuseidentifier:@</code><code>"cellid"</code><code>];</code>
<code> </code>
<code> </code><code>[self.view addsubview:collect];</code>
<code>-(nsinteger)numberofsectionsincollectionview:(uicollectionview *)collectionview{</code>
<code> </code><code>return</code> <code>1;</code>
<code>-(nsinteger)collectionview:(uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section{</code>
<code> </code><code>return</code> <code>100;</code>
<code>-(uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath{</code>
<code> </code><code>uicollectionviewcell * cell = [collectionview dequeuereusablecellwithreuseidentifier:@</code><code>"cellid"</code> <code>forindexpath:indexpath];</code>
<code> </code><code>cell.backgroundcolor = [uicolor colorwithred:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];</code>
<code> </code><code>return</code> <code>cell;</code>
运行效果就是我们引言中的截图。
通过上面的例子,我们可以了解,collectionview的item布局其实是layoutattributes类具体配置的,这个类可以配置的布局属性不止是frame这么简单,其中还有许多属性:
<code>//配置item的布局位置</code>
<code>@property (nonatomic) cgrect frame;</code>
<code>//配置item的中心</code>
<code>@property (nonatomic) cgpoint center;</code>
<code>//配置item的尺寸</code>
<code>@property (nonatomic) cgsize size;</code>
<code>//配置item的3d效果</code>
<code>@property (nonatomic) catransform3d transform3d;</code>
<code>//配置item的bounds</code>
<code>@property (nonatomic) cgrect bounds ns_available_ios(7_0);</code>
<code>//配置item的旋转</code>
<code>@property (nonatomic) cgaffinetransform transform ns_available_ios(7_0);</code>
<code>//配置item的alpha</code>
<code>@property (nonatomic) cgfloat alpha;</code>
<code>//配置item的z坐标</code>
<code>@property (nonatomic) nsinteger zindex; </code><code>// default is 0</code>
<code>//配置item的隐藏</code>
<code>@property (nonatomic, getter=ishidden) </code><code>bool</code> <code>hidden; </code>
<code>//item的indexpath</code>
<code>@property (nonatomic, strong) nsindexpath *indexpath;</code>
<code>//获取item的类型</code>
<code>@property (nonatomic, readonly) uicollectionelementcategory representedelementcategory;</code>
<code>@property (nonatomic, readonly, nullable) nsstring *representedelementkind; </code>
<code>//一些创建方法</code>
<code>+ (instancetype)layoutattributesforcellwithindexpath:(nsindexpath *)indexpath;</code>
<code>+ (instancetype)layoutattributesforsupplementaryviewofkind:(nsstring *)elementkind withindexpath:(nsindexpath *)indexpath;</code>
<code>+ (instancetype)layoutattributesfordecorationviewofkind:(nsstring *)decorationviewkind withindexpath:(nsindexpath *)indexpath;</code>
通过上面的属性,可以布局出各式各样的炫酷效果,正如一句话:没有做不到,只有想不到。