這段時間突然想到一個很久之前用到的知識-瀑布流,本來想用一個簡單的方法,發(fā)現(xiàn)自己走入了歧途,最終只能狠下心來重寫uicollectionviewflowlayout.下面我將用兩種方法實現(xiàn)瀑布流,以及會介紹第一種實現(xiàn)的bug.
<1>第一種
效果圖如下所示:
這種實現(xiàn)方法的思路:
1)首先調(diào)用隨機函數(shù),產(chǎn)生隨機高度,并把它保存到數(shù)組中
1
2
3
4
5
6
7
8
|
- (cgsize)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout sizeforitematindexpath:(nsindexpath *)indexpath { cgfloat cellw = 100; cgfloat cellh = 100 + (arc4random() % 80); [self.heightarraym addobject:@(cellh)]; return cgsizemake(cellw, cellh); } |
2)在設置cell的frame的地方,通過取余,取整確定cell的高度,并設定cell的frame
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
- (uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath { uicollectionviewcell *cell = [self.collectionview dequeuereusablecellwithreuseidentifier:id forindexpath:indexpath]; //當前處于多少行 nsinteger num1 = indexpath.row / count; //當前處于多少列 int num2 = indexpath.row % count; cgfloat cellx = num2 * 100 + (num2 + 1) * margin; cgfloat celly = 0; for ( int i = 0; i < num1; i++) { nsinteger position = num2 + i * 3; celly += [self.heightarraym[position] floatvalue] + margin; } cgfloat cellw = 100; cgfloat cellh = cellheight; cell.frame = cgrectmake(cellx, celly, cellw, cellh); // cell.backgroundcolor = [uicolor redcolor]; cell.backgroundcolor = [uicolor colorwithred:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0]; // nslog(@"%@", nsstringfromcgrect(cell.frame)); return cell; } |
弊端 : 其實這種方法的弊端,相信從上面的動態(tài)圖中可以看出來,當往上面滑的時候,由于cell的循環(huán)機制,下面的cell的會消失,但是由于高度不一致,同時撤銷的是最后一行的cell,所以下面的cell在屏幕上就會消失.
下面附上第一種方法的源代碼:
1
2
3
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
#import "viewcontroller.h" #define margin 10 #define count 3 #define cellheight [self.heightarraym[indexpath.row] floatvalue] static nsstring * const id = @ "cell" ; @interface viewcontroller ()<uicollectionviewdelegate, uicollectionviewdatasource, uicollectionviewdelegateflowlayout> @property (weak, nonatomic) iboutlet uicollectionview *collectionview; @property (nonatomic, strong) nsmutablearray *heightarraym; @end @implementation viewcontroller - (nsmutablearray *)heightarraym { if (_heightarraym == nil) { _heightarraym = [nsmutablearray array]; } return _heightarraym; } - ( void )viewdidload { [super viewdidload]; [self.collectionview registerclass:[uicollectionviewcell class ] forcellwithreuseidentifier:id]; self.collectionview.datasource = self; self.collectionview.delegate = self; //設置collectionview [self setupcollectionview]; } //設置collectionview的布局 - (uicollectionviewflowlayout *)setupcollectionlayout { uicollectionviewflowlayout *flowlayout = [[uicollectionviewflowlayout alloc] init]; flowlayout.minimuminteritemspacing = margin; flowlayout.minimumlinespacing = margin; flowlayout.sectioninset = uiedgeinsetsmake(margin, margin, margin, margin); return flowlayout; } //設置collectionview - ( void )setupcollectionview { self.collectionview.collectionviewlayout =[self setupcollectionlayout]; } #pragma mark - uicollectionviewdatasouce - (nsinteger)collectionview:(uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section { return 60; } - (uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath { uicollectionviewcell *cell = [self.collectionview dequeuereusablecellwithreuseidentifier:id forindexpath:indexpath]; //當前處于多少行 nsinteger num1 = indexpath.row / count; //當前處于多少列 int num2 = indexpath.row % count; cgfloat cellx = num2 * 100 + (num2 + 1) * margin; cgfloat celly = 0; for ( int i = 0; i < num1; i++) { nsinteger position = num2 + i * 3; celly += [self.heightarraym[position] floatvalue] + margin; } cgfloat cellw = 100; cgfloat cellh = cellheight; cell.frame = cgrectmake(cellx, celly, cellw, cellh); // cell.backgroundcolor = [uicolor redcolor]; cell.backgroundcolor = [uicolor colorwithred:(arc4random() % 250) / 250.0 green:(arc4random() % 250) / 250.0 blue:(arc4random() % 250) / 250.0 alpha:1.0]; // nslog(@"%@", nsstringfromcgrect(cell.frame)); return cell; } - (cgsize)collectionview:(uicollectionview *)collectionview layout:(uicollectionviewlayout *)collectionviewlayout sizeforitematindexpath:(nsindexpath *)indexpath { cgfloat cellw = 100; cgfloat cellh = 100 + (arc4random() % 80); [self.heightarraym addobject:@(cellh)]; return cgsizemake(cellw, cellh); } @end |
<2>下面介紹第二種(swift實現(xiàn))
效果圖如下所示:
這種實現(xiàn)方法就是比較成熟的了,我把它封裝成一個類.其實主要是實現(xiàn)三個函數(shù)
1)重寫父類的prepare方法,準備所有cell的樣式
1
2
3
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
|
extension waterfalllayout { // prepare準備所有cell的布局樣式 override func prepare() { super.prepare() // 0.獲取item的個數(shù) let itemcount = collectionview!.numberofitems(insection: 0) // 1.獲取列數(shù) let cols = datasource?.numberofcolsinwaterfalllayout?(self) ?? 2 // 2.計算item的寬度 let itemw = (collectionview!.bounds.width - self.sectioninset.left - self.sectioninset.right - self.minimuminteritemspacing * cgfloat((cols - 1))) / cgfloat(cols) // 3.計算所有的item的屬性 for i in startindex..<itemcount { // 1.設置每一個item位置相關的屬性 let indexpath = indexpath(item: i, section: 0) // 2.根據(jù)位置創(chuàng)建attributes屬性 let attrs = uicollectionviewlayoutattributes(forcellwith: indexpath) // 3.隨機一個高度 guard let height = datasource?.waterfalllayout(self, indexpath: indexpath) else { fatalerror( "請設置數(shù)據(jù)源,并且實現(xiàn)對應的數(shù)據(jù)源方法" ) } // 4.取出最小列的位置 var minh = colheights.min()! let index = colheights.index(of: minh)! minh = minh + height + minimumlinespacing colheights[index] = minh // 5.設置item的屬性 attrs.frame = cgrect(x: self.sectioninset.left + (self.minimuminteritemspacing + itemw) * cgfloat(index), y: minh - height - self.minimumlinespacing, width: itemw, height: height) attrsarray.append(attrs) } // 4.記錄最大值 maxh = colheights.max()! // 5.給startindex重新復制 startindex = itemcount } } |
2)返回設置cell樣式的數(shù)組
1
2
3
|
override func layoutattributesforelements(in rect: cgrect) -> [uicollectionviewlayoutattributes]? { return attrsarray } |
3)返回當前的contentsize
1
2
3
|
override var collectionviewcontentsize: cgsize { return cgsize(width: 0, height: maxh + sectioninset.bottom - minimumlinespacing) } |
總結(jié):
在下面我封裝的這個類中,只需要遵守我的數(shù)據(jù)代理源協(xié)議并且實現(xiàn)我的協(xié)議中的兩個方法,傳給我對應得高度(我這里是傳的隨機的),可選的方法,若是不實現(xiàn),會有一個默認值,就可以實現(xiàn)該功能.協(xié)議如下:
1
2
3
4
|
@objc protocol waterfalllayoutdatasource : class { func waterfalllayout(_ layout : waterfalllayout, indexpath : indexpath) -> cgfloat @objc optional func numberofcolsinwaterfalllayout(_ layout : waterfalllayout) -> int } |
完成代碼如下所示:
viewcontroller.swift中的代碼:
1
2
3
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
57
58
59
60
61
62
63
|
import uikit extension uicolor { class func randomcolor() -> uicolor { return uicolor(colorliteralred: float (arc4random_uniform(256)) / 255.0, green: float (arc4random_uniform(256)) / 255.0, blue: float (arc4random_uniform(256)) / 255.0, alpha: 1.0) } } private let kwatercellid = "kwatercellid" class viewcontroller: uiviewcontroller { var count : int = 20 override func viewdidload() { super.viewdidload() // 1.設置布局 let layout = waterfalllayout() layout.minimumlinespacing = 10 layout.minimuminteritemspacing = 10 layout.sectioninset = uiedgeinsets(top: 10, left: 10, bottom: 10, right: 10) layout.datasource = self // 2.創(chuàng)建uicollectionview let collectionview = uicollectionview(frame: view.bounds, collectionviewlayout: layout) collectionview.datasource = self collectionview. register (uicollectionviewcell.self, forcellwithreuseidentifier: kwatercellid) view.addsubview(collectionview) } } extension viewcontroller : uicollectionviewdatasource { func collectionview(_ collectionview: uicollectionview, numberofitemsinsection section: int ) -> int { return count } func collectionview(_ collectionview: uicollectionview, cellforitemat indexpath: indexpath) -> uicollectionviewcell { let cell = collectionview.dequeuereusablecell(withreuseidentifier: kwatercellid, for : indexpath) cell.backgroundcolor = uicolor.randomcolor() if indexpath.item == count - 1 { count += 20 collectionview.reloaddata() } return cell } } extension viewcontroller : waterfalllayoutdatasource { func waterfalllayout(_ layout: waterfalllayout, indexpath: indexpath) -> cgfloat { return cgfloat(arc4random_uniform(80) + 100) } func numberofcolsinwaterfalllayout(_ layout: waterfalllayout) -> int { return 3 } } |
封裝自定義布局中的waterfalllayout.swift代碼如下:
1
2
3
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
import uikit @objc protocol waterfalllayoutdatasource : class { func waterfalllayout(_ layout : waterfalllayout, indexpath : indexpath) -> cgfloat @objc optional func numberofcolsinwaterfalllayout(_ layout : waterfalllayout) -> int } class waterfalllayout: uicollectionviewflowlayout { // mark: 對外提供屬性 weak var datasource : waterfalllayoutdatasource? // mark: 私有屬性 fileprivate lazy var attrsarray : [uicollectionviewlayoutattributes] = [uicollectionviewlayoutattributes]() fileprivate var totalheight : cgfloat = 0 fileprivate lazy var colheights : [cgfloat] = { let cols = self.datasource?.numberofcolsinwaterfalllayout?(self) ?? 2 var colheights = array(repeating: self.sectioninset.top, count: cols) return colheights }() fileprivate var maxh : cgfloat = 0 fileprivate var startindex = 0 } extension waterfalllayout { // prepare準備所有cell的布局樣式 override func prepare() { super.prepare() // 0.獲取item的個數(shù) let itemcount = collectionview!.numberofitems(insection: 0) // 1.獲取列數(shù) let cols = datasource?.numberofcolsinwaterfalllayout?(self) ?? 2 // 2.計算item的寬度 let itemw = (collectionview!.bounds.width - self.sectioninset.left - self.sectioninset.right - self.minimuminteritemspacing * cgfloat((cols - 1))) / cgfloat(cols) // 3.計算所有的item的屬性 for i in startindex..<itemcount { // 1.設置每一個item位置相關的屬性 let indexpath = indexpath(item: i, section: 0) // 2.根據(jù)位置創(chuàng)建attributes屬性 let attrs = uicollectionviewlayoutattributes(forcellwith: indexpath) // 3.隨機一個高度 guard let height = datasource?.waterfalllayout(self, indexpath: indexpath) else { fatalerror( "請設置數(shù)據(jù)源,并且實現(xiàn)對應的數(shù)據(jù)源方法" ) } // 4.取出最小列的位置 var minh = colheights.min()! let index = colheights.index(of: minh)! minh = minh + height + minimumlinespacing colheights[index] = minh // 5.設置item的屬性 attrs.frame = cgrect(x: self.sectioninset.left + (self.minimuminteritemspacing + itemw) * cgfloat(index), y: minh - height - self.minimumlinespacing, width: itemw, height: height) attrsarray.append(attrs) } // 4.記錄最大值 maxh = colheights.max()! // 5.給startindex重新復制 startindex = itemcount } } extension waterfalllayout { override func layoutattributesforelements(in rect: cgrect) -> [uicollectionviewlayoutattributes]? { return attrsarray } override var collectionviewcontentsize: cgsize { return cgsize(width: 0, height: maxh + sectioninset.bottom - minimumlinespacing) } } |
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/muzichenyu/p/6108040.html