我在前面多篇博客中詳細講解了CollectionView的使用與自定義CollectionViewCell的設計,能夠參考《iOS開發實戰——CollectionView點擊事件與鍵盤隱藏結合案例》《iOS高級開發——CollectionView修改cell的文本及模型重構》這幾篇博客。可是今天仍是須要來說講CollectionView實現中的一個小小的坑,這是我最近在網上瀏覽時發現不少開發者常常犯的錯,因此我以爲有必要來好好談一談。git
一個CollectionView控件中,兩個cell之間的間距如何設置?這是一個很常見的問題。當咱們在網上搜這個問題的時候,不少人告訴你使用UICollectionViewDelegateFlowLayout這個代理中的minimumInteritemSpacingForSectionAtIndex方法來設置。其實這徹底錯了,當你真的用這個方法去設置間距的時候,發現怎麼都設置不成本身的需求,真的是截然不同。本篇博客就要來解決這個問題,示例代碼上傳至 https://github.com/chenyufeng1991/SpaceOfCollectionView 。github
(1)首先自定義一個CollectionViewCell,繼承自UICollectionViewCell。在這個cell中放一張圖片填充,該文件定義爲CustomCollectionViewCell.數組
CustomCollectionViewCell.h文件以下:佈局
- #import <UIKit/UIKit.h>
-
- #define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
-
- #define CELL_WIDTH01 (SCREEN_WIDTH - 80) / 3
- #define CELL_WIDTH02 70
-
- @interface CustomCollectionViewCell : UICollectionViewCell
-
- @property (nonatomic, strong) UIImageView *imageView;
-
- @end
CustomCollectionViewCell.m文件以下:測試
- #import "CustomCollectionViewCell.h"
- #import "Masonry.h"
-
- @implementation CustomCollectionViewCell
-
- - (instancetype)initWithFrame:(CGRect)frame
- {
- self = [super initWithFrame:frame];
- if (self)
- {
- self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CELL_WIDTH02, CELL_WIDTH02)];
- [self addSubview:self.imageView];
- // 圖片填充滿整個cell
- [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.edges.equalTo(self);
- }];
- }
- return self;
- }
-
- @end
上面的宏定義SCREEN_WIDTH表示屏幕寬度。CELL_WIDTH01和CELL_WIDTH02能夠用來設置cell的寬高。atom
(2)個人主文件爲MainViewController.m,首先須要聲明三個代理:.net
UICollectionViewDelegate,設計
UICollectionViewDataSource,代理
UICollectionViewDelegateFlowLayoutblog
(3)MainViewController.m中聲明兩個屬性:
- @property (nonatomic, strong) UICollectionView *collectionView;
- @property (nonatomic, strong) NSMutableArray *collArr;
(4)宏定義幾個變量:
- #define ARRAY_COUNT 10
- #define MINIMUM_ITEM_SPACE 5
- #define MINIMUM_LINE_SPACE 5
ARRAY_COUNT是cell的數量;
MINIMUM_ITEM_SPACE是設置cell之間的最小間距(等下我解釋這個概念)
MINIMUM_LINE_SPACE是設置每行之間的間距。
(5)UI佈局
- - (void)configUI
- {
- // CollectionView
- self.collArr = [[NSMutableArray alloc] init];
- for (int i = 0; i < ARRAY_COUNT; i++)
- {
- [self.collArr addObject:[UIImage imageNamed:@"beauty"]];
- }
-
- UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
- [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];// CollectionView的滾動方向,只能二選一
-
- // 初始化,能夠不設置寬高,經過下面的mas_makeConstraints自動佈局完成。
- self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 100, SCREEN_WIDTH, CELL_WIDTH02 * 2) collectionViewLayout:flowLayout];
- self.collectionView.bounces = NO;
- [self.collectionView registerClass:[CustomCollectionViewCell class] forCellWithReuseIdentifier:@"CollectionCell"];
- self.collectionView.delegate = self;
- self.collectionView.dataSource = self;
- [self.view addSubview:self.collectionView];
- [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {
- make.top.equalTo(self.view).offset(64);
- make.left.equalTo(self.view);
- make.right.equalTo(self.view);
- make.height.equalTo(@(CELL_WIDTH02 * 2));
- }];
- }
cell的數量能夠經過宏定義ARRAY_COUNT來設置。
這裏的Autolayout我使用Masonry進行自動佈局,關於Masonry的使用能夠參考《Autolayout第三方庫Masonry的入門與實踐》。
這裏我設置UICollection的寬度爲屏幕寬度,高度爲2 * CELL_WIDTH02.
UICollectionViewFlowLayout是設置CollectionView內部cell的佈局,必須進行設置。
(6)UICollectionViewDataSource代理中方法重寫:
- #pragma mark - UiCollectionViewDataSource
- - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
- {
- return self.collArr.count;
- }
-
- - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
- {
- CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath]; // 重用cell
- if (cell == nil)
- {
- // 當重用cell爲空時,建立新的cell
- cell = [[CustomCollectionViewCell alloc] init];
- }
- cell.imageView.image = self.collArr[indexPath.row];
-
- return cell;
- }
numberOfItemsInSection是設置section中cell的數量,返回數組數量便可,在這裏其實就是ARRAY_COUNT宏定義。
cellForItemAtIndexPath是爲cell進行賦值。
(7)UICollectionViewDelegateFlowLayout代理中方法的重寫:
- #pragma mark - UICollectionViewDelegateFlowLayout
-
- // 該方法是設置cell的size
- - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
- {
- return CGSizeMake(CELL_WIDTH02, CELL_WIDTH02);
- }
-
- // 該方法是設置一個section的上左下右邊距
- - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
- {
- // 注意,這裏默認會在top 有+64的邊距,由於狀態欄+導航欄是64.
- // 由於咱們經常把[[UIScreen mainScreen] bounds]做爲CollectionView的區域,因此蘋果API就默認給了+64的EdgeInsets,這裏實際上是一個坑,必定要注意。
- // 這裏我暫時不用這個邊距,因此top減去64
- // 因此這是就要考慮你是把Collection從屏幕左上角(0,0)開始放仍是(0,64)開始放。
- return UIEdgeInsetsMake(-64, 0, 0, 0);
- }
-
- // 兩個cell之間的最小間距,是由API自動計算的,只有當間距小於該值時,cell會進行換行
- - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
- {
- return MINIMUM_ITEM_SPACE;
- }
-
- // 兩行之間的最小間距
- - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
- {
- return MINIMUM_LINE_SPACE;
- }
-- sizeForItemAtIndexPath方法是設置cell的寬高,這裏我設置成CELL_WIDTH02;
-- insetForSectionAtIndex方法是設置一個section在CollectionView中的內邊距;
-- minimumInteritemSpacingForSectionAtIndex方法是設置cell之間的最小邊距(我下面會詳細講解這個方法);
-- minimumLineSpacingForSectionAtIndex方法是設置每行之間的距離;
(8)完成以上代碼後 ,實現的效果以下:
。---------------------------------------------------------------------------------------------------------(打個分割線,其實以上都是鋪墊,下面正式講如何設置cell之間的邊距)
其實準確的說,cell之間的間距不是咱們手動設置的,也沒有辦法手動設置,是由系統爲咱們計算的。計算方式以下:
CollectionView寬度:CollectionWidth,
一個Cell寬度:CellWidth,
一行cell的個數:N,
cell的間距:SpaceX,
cell的最小間距:MinimumX
公式以下:SpaceX = CollectionWidth - CellWidth * N(N爲正整數); 當 SpaceX >= MinimumX時,N會遞增,也就是說一行的cell數量會增多。系統會進行檢測,當增長一個cell時,SpaceX < MinimumX時(實際間距絕對不能小於這個最小間距),cell就會進行換行,因此N會取容許範圍內的最大值。因而可知,對於某一個固定的CollectionView,cell的間距只是由cell的寬度決定的。
在我上面的例子中,是在5s模擬器下測試的,屏幕寬度爲320,cell寬度爲70,minimumInteritemSpacingForSectionAtIndex方法設置的最小cell間距爲5,因此一行只能放下4個cell,而且cell之間的間距=(320-70*4)/3 = 13.33 > 5 .而且不能放5個,由於 5 * 70 > 320了。固然cell也不可能爲3個,由於能夠設置的最大值爲4個。我這裏經過截圖來驗證這個是否正確:
。
經過截圖驗證,咱們的計算是正確的,由於是兩倍像素:因此2*13 = 26.
好,爲了再次說明個人說法的正確性,minimumInteritemSpacingForSectionAtIndex方法的返回值能夠爲13如下的任何正整數,最後運行的UI都是同樣的。由於當cell=4的時候,系統計算的13.33始終大於minimumInteritemSpacingForSectionAtIndex返回值。
如今修改代碼,minimumInteritemSpacingForSectionAtIndex返回值爲14,從新運行程序,UI效果以下:
- // 兩個cell之間的最小間距,是由API自動計算的,只有當間距小於該值時,cell會進行換行
- - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
- {
- return 14.0f;
- }
。
能夠看到一行的cell只有3個了。由於當一行cell=4的時候,間距13.33小於了我設置的最小間距14,因此係統不得不一行減小一個cell來知足這個最小間距的要求。如今計算一下間距:(320 - 3 * 70) /2 = 55 > 14最小間距,知足條件, 經過截圖來進行驗證:
。
實際證實計算仍舊是正確的。
如今你們應該明白了minimumInteritemSpacingForSectionAtIndex這個方法的含義了吧,也知道了如何去調整cell的間距。因此綜上所述一下,對於控制CollectionView中一行顯示的cell數量和cell間距,只能經過cell的寬高和minimumInteritemSpacingForSectionAtIndex設置最小間距來完成,而不是經過代碼手動設置的,計算任務就交給系統吧。