天天看點

iOS流布局UICollectionView系列四——自定義FlowLayout進行瀑布流布局

        前幾篇部落格從uicollectionview的基礎應用到設定uicollectionviewflowlayout更加靈活的進行布局,但都限制在系統為我們準備好的布局架構中,還是有一些局限性,例如,如果我要進行瀑布流似的不定高布局,前面的方法就很難滿足我們的需求了,如下:

iOS流布局UICollectionView系列四——自定義FlowLayout進行瀑布流布局

這種布局無疑在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&lt;_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]&lt;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]&gt;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&lt;uicollectionviewlayoutattributes *&gt; *)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>

通過上面的屬性,可以布局出各式各樣的炫酷效果,正如一句話:沒有做不到,隻有想不到。

繼續閱讀