UICollectionView之因此強大,是由於其具備自定義功能,這一自定義就不得了啦,自由度很是大,定製的高,因此功能也是灰常強大的。本篇博客就不使用自帶的流式佈局了,咱們要自定義一個瀑布流。自定義的瀑布流能夠配置其參數: 每一個Cell的邊距,共有多少列,Cell的最大以及最小高度是多少等。git
先來看一下不一樣配置參數下運行後的效果吧,每張截圖的列數和Cell之間的邊距都有所不一樣,瀑布流的列數依次爲2,3,8。有密集恐懼證的童鞋就不要看這些運行效果圖了,真的會看暈的。下面這些運行效果就是修改不一樣的配置參數來進行佈局的。看圖吧,關於瀑布流的效果就不囉嗦了。如下的效果就是使用自定義佈局作的,接下來將會介紹一下其實現原理。github
在介紹上述效果實現原理以前,須要介紹一下UICollectionViewLayout。UICollectionView的自定義功能就是本身去實現UICollectionViewLayout的子類,而後重寫相應的方法來實現Cell的佈局,先介紹一下須要重寫的方法,而後再此方法上進行應用實現上述瀑布流。好,廢話少說,幹活走起。數組
當佈局首次被加載時會調用prepareLayout函數,見名知意,就是預先加載佈局,在該方法中能夠去初始化佈局相關的數據。該方法相似於視圖控制器的ViewDidLoad方法,稍後回用到該方法。bash
1 // The collection view calls -prepareLayout once at its first layout as the first message to the layout instance.
2 // The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information.
3 // Subclasses should always call super if they override.
4 - (void)prepareLayout;
複製代碼
下方是定義ContentSize的方法。該方法會返回CollectionView的大小,這個方法也是自定義佈局中必須實現的方法。說白了,就是設置ScrollView的ContentSize,即滾動區域。dom
1 // 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 to facilitate scrolling.
2 - (CGSize)collectionViewContentSize;
複製代碼
下方四個方法是肯定佈局屬性的,下方第一個方法返回一個數組,該數組中存放的是爲每一個Cell綁定的UICollectionViewLayoutAttributes屬性,便於在下面第二個方法中去定製每一個Cell的屬性。第三個方法就是根據indexPath來獲取Cell所綁定的layoutAtrributes, 而後去更改UICollectionViewLayoutAttributes對象的一些屬性並返回,第四個是爲Header View或者FooterView來定製其對應的UICollectionViewLayoutAttributes,而後返回。ide
1 // UICollectionView calls these four methods to determine the layout information.
2 // Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
3 // Additionally, all layout subclasses should implement -layoutAttributesForItemAtIndexPath: to return layout attributes instances on demand for specific index paths.
4 // If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
5 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect
6 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
7 - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
8 - (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;
複製代碼
下方是UICollectionViewLayoutAttributes經常使用的屬性,你能夠在上面第二個方法中去爲下方這些屬性賦值,爲Cell定製屬於本身的Attributes。由下方的屬性就對自定義佈局的的強大,在本篇博客中只用到了下方的一個屬性,那就是frame。函數
1 @property (nonatomic) CGRect frame;
2 @property (nonatomic) CGPoint center;
3 @property (nonatomic) CGSize size;
4 @property (nonatomic) CATransform3D transform3D;
5 @property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0);
6 @property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
7 @property (nonatomic) CGFloat alpha;
8 @property (nonatomic) NSInteger zIndex; // default is 0
9 @property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES
複製代碼
通過上面的簡單介紹,想必對UICollectionViewLayout有必定的瞭解吧,UICollectionViewLayout中還有好多方法,之後用到的時候在給你們介紹。接下來要使用自定義佈局來實現瀑布流。咱們須要在UICollectionViewLayout的子類中實現相應的佈局方法,由於UICollectionViewLayout是虛基類,是不能直接被實例化的,因此咱們須要新建一個佈局類,這個佈局類繼承自UICollectionViewLayout。而後去實現上述方法,給每一個Cell定製不一樣的UICollectionViewLayoutAttributes。好了仍是拿代碼說話吧。佈局
1 #pragma mark -- <UICollectionViewLayout>虛基類中重寫的方法
2
3 /**
4 * 該方法是預加載layout, 只會被執行一次
5 */
6 - (void)prepareLayout{
7 [super prepareLayout];
8
9 [self initData];
10
11 [self initCellWidth];
12
13 [self initCellHeight];
14
15 }
複製代碼
1 /**
2 * 該方法返回CollectionView的ContentSize的大小
3 */
4 - (CGSize)collectionViewContentSize{
5
6 CGFloat height = [self maxCellYArrayWithArray:_cellYArray];
7
8 return CGSizeMake(SCREEN_WIDTH, height);
9 }
複製代碼
1 /**
2 * 該方法爲每一個Cell綁定一個Layout屬性~
3 */
4 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
5 {
6
7 [self initCellYArray];
8
9 NSMutableArray *array = [NSMutableArray array];
10
11 //add cells
12 for (int i=0; i < _numberOfCellsInSections; i++)
13 {
14 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
15
16 UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
17
18 [array addObject:attributes];
19 }
20
21 return array;
22
23 }
複製代碼
(1)Cell寬度計算:若是瀑布流的列數和Cell的Padding肯定了,那麼每一個Cell的寬度再經過屏幕的寬度就能夠計算出來了。post
(2)Cell高度計算:經過隨機數生成的高度優化
(3)Cell的X軸座標計算:經過列數,和Padding,以及每一個Cell的寬度很容易就能夠計算出每一個Cell的X座標。
(4)Cell的Y軸座標計算:經過Cell所在列的上一個Cell的Y軸座標,Padding, 和 上一個Cell的高度就能夠計算下一個Cell的Y座標,並記錄在Y座標的數組中了。
/**
* 該方法爲每一個Cell綁定一個Layout屬性~
*/
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
CGRect frame = CGRectZero;
CGFloat cellHeight = [_cellHeightArray[indexPath.row] floatValue];
NSInteger minYIndex = [self minCellYArrayWithArray:_cellYArray];
CGFloat tempX = [_cellXArray[minYIndex] floatValue];
CGFloat tempY = [_cellYArray[minYIndex] floatValue];
frame = CGRectMake(tempX, tempY, _cellWidth, cellHeight);
//更新相應的Y座標
_cellYArray[minYIndex] = @(tempY + cellHeight + _padding);
//計算每一個Cell的位置
attributes.frame = frame;
return attributes;
}
複製代碼
/**
* 初始化相關數據
*/
- (void) initData{
_numberOfSections = [self.collectionView numberOfSections];
_numberOfCellsInSections = [self.collectionView numberOfItemsInSection:0];
//經過回調獲取列數
_columnCount = 5;
_padding = 5;
_cellMinHeight = 50;
_cellMaxHeight = 150;
}
複製代碼
1 /**
2 * 根據Cell的列數求出Cell的寬度
3 */
4 - (void) initCellWidth{
5 //計算每一個Cell的寬度
6 _cellWidth = (SCREEN_WIDTH - (_columnCount -1) * _padding) / _columnCount;
7
8 //爲每一個Cell計算X座標
9 _cellXArray = [[NSMutableArray alloc] initWithCapacity:_columnCount];
10 for (int i = 0; i < _columnCount; i ++) {
11
12 CGFloat tempX = i * (_cellWidth + _padding);
13
14 [_cellXArray addObject:@(tempX)];
15 }
16
17 }
複製代碼
1 /**
2 * 隨機生成Cell的高度
3 */
4 - (void) initCellHeight{
5 //隨機生成Cell的高度
6 _cellHeightArray = [[NSMutableArray alloc] initWithCapacity:_numberOfCellsInSections];
7 for (int i = 0; i < _numberOfCellsInSections; i ++) {
8
9 CGFloat cellHeight = arc4random() % (_cellMaxHeight - _cellMinHeight) + _cellMinHeight;
10
11 [_cellHeightArray addObject:@(cellHeight)];
12 }
13
14 }
複製代碼
/**
* 初始化每列Cell的Y軸座標
*/
- (void) initCellYArray{
_cellYArray = [[NSMutableArray alloc] initWithCapacity:_columnCount];
for (int i = 0; i < _columnCount; i ++) {
[_cellYArray addObject:@(0)];
}
}
複製代碼
1 /**
2 * 求CellY數組中的最大值並返回
3 */
4 - (CGFloat) maxCellYArrayWithArray: (NSMutableArray *) array{
5 if (array.count == 0) {
6 return 0.0f;
7 }
8
9 CGFloat max = [array[0] floatValue];
10 for (NSNumber *number in array) {
11
12 CGFloat temp = [number floatValue];
13
14 if (max < temp) {
15 max = temp;
16 }
17 }
18
19 return max;
20 }
複製代碼
1 /**
2 * 求CellY數組中的最小值的索引
3 */
4 - (CGFloat) minCellYArrayWithArray: (NSMutableArray *) array{
5
6 if (array.count == 0) {
7 return 0.0f;
8 }
9
10 NSInteger minIndex = 0;
11 CGFloat min = [array[0] floatValue];
12
13 for (int i = 0; i < array.count; i ++) {
14 CGFloat temp = [array[i] floatValue];
15
16 if (min > temp) {
17 min = temp;
18 minIndex = i;
19 }
20 }
21
22 return minIndex;
23 }
複製代碼
自定義集合視圖控制器佈局第一階段就先到這,下篇博客會在此基礎上進一步開發。把上述寫死的配置參數,經過Delegate提供,使其在UICollectionView可進行配置,其配置方式相似於UICollectionViewDelegateFlowLayout的代理方法。
上述代碼gitHub分享地址:github.com/lizelu/Cust…
你認爲如何?請經過加咱們的交流羣 點擊此處進交流羣 ,來一塊兒交流或者發佈您的問題,意見或反饋。
做者:青玉伏案 出處:www.cnblogs.com/ludashi/