iOS开发实战——CollectionView中cell的间距设置

我在前面多篇博客中详细讲解了CollectionView的使用与自定义CollectionViewCell的设计,可以参考《iOS开发实战——CollectionView点击事件与键盘隐藏结合案例》《iOS高级开发——CollectionView修改cell的文本及模型重构》这几篇博客。但是今天还是需要来讲讲CollectionView实现中的一个小小的坑,这是我最近在网上浏览时发现很多开发者经常犯的错,所以我觉得有必要来好好谈一谈。

       一个CollectionView控件中,两个cell之间的间距如何设置?这是一个很常见的问题。当我们在网上搜这个问题的时候,很多人告诉你使用UICollectionViewDelegateFlowLayout这个代理中的minimumInteritemSpacingForSectionAtIndex方法来设置。其实这完全错了,当你真的用这个方法去设置间距的时候,发现怎么都设置不成自己的需求,真的是大相径庭。本篇博客就要来解决这个问题,示例代码上传至 https://github.com/chenyufeng1991/SpaceOfCollectionView 。

(1)首先自定义一个CollectionViewCell,继承自UICollectionViewCell。在这个cell中放一张图片填充,该文件定义为CustomCollectionViewCell.

CustomCollectionViewCell.h文件如下:

  1. #import <UIKit/UIKit.h>  
  2.   
  3. #define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)  
  4.   
  5. #define CELL_WIDTH01 (SCREEN_WIDTH - 80) / 3  
  6. #define CELL_WIDTH02 70  
  7.   
  8. @interface CustomCollectionViewCell : UICollectionViewCell  
  9.   
  10. @property (nonatomic, strong) UIImageView *imageView;  
  11.   
  12. @end  


CustomCollectionViewCell.m文件如下:

  1. #import "CustomCollectionViewCell.h"  
  2. #import "Masonry.h"  
  3.   
  4. @implementation CustomCollectionViewCell  
  5.   
  6. - (instancetype)initWithFrame:(CGRect)frame  
  7. {  
  8.     self = [super initWithFrame:frame];  
  9.     if (self)  
  10.     {  
  11.         self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CELL_WIDTH02, CELL_WIDTH02)];  
  12.         [self addSubview:self.imageView];  
  13.         // 图片填充满整个cell  
  14.         [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {  
  15.             make.edges.equalTo(self);  
  16.         }];  
  17.     }  
  18.     return self;  
  19. }  
  20.   
  21. @end  


上面的宏定义SCREEN_WIDTH表示屏幕宽度。CELL_WIDTH01和CELL_WIDTH02可以用来设置cell的宽高。

(2)我的主文件为MainViewController.m,首先需要声明三个代理:

UICollectionViewDelegate,

UICollectionViewDataSource,

UICollectionViewDelegateFlowLayout

 

(3)MainViewController.m中声明两个属性:

  1. @property (nonatomic, strong) UICollectionView *collectionView;  
  2. @property (nonatomic, strong) NSMutableArray *collArr;  


(4)宏定义几个变量:

  1. #define ARRAY_COUNT 10  
  2. #define MINIMUM_ITEM_SPACE 5  
  3. #define MINIMUM_LINE_SPACE 5  


ARRAY_COUNT是cell的数量;

 

MINIMUM_ITEM_SPACE是设置cell之间的最小间距(等下我解释这个概念)

MINIMUM_LINE_SPACE是设置每行之间的间距。

 

(5)UI布局

  1. - (void)configUI  
  2. {  
  3.     // CollectionView  
  4.     self.collArr = [[NSMutableArray alloc] init];  
  5.     for (int i = 0; i < ARRAY_COUNT; i++)  
  6.     {  
  7.         [self.collArr addObject:[UIImage imageNamed:@"beauty"]];  
  8.     }  
  9.   
  10.     UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];  
  11.     [flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];// CollectionView的滚动方向,只能二选一  
  12.   
  13.     // 初始化,可以不设置宽高,通过下面的mas_makeConstraints自动布局完成。  
  14.     self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 100, SCREEN_WIDTH, CELL_WIDTH02 * 2) collectionViewLayout:flowLayout];  
  15.     self.collectionView.bounces = NO;  
  16.     [self.collectionView registerClass:[CustomCollectionViewCell class] forCellWithReuseIdentifier:@"CollectionCell"];  
  17.     self.collectionView.delegate = self;  
  18.     self.collectionView.dataSource = self;  
  19.     [self.view addSubview:self.collectionView];  
  20.     [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) {  
  21.         make.top.equalTo(self.view).offset(64);  
  22.         make.left.equalTo(self.view);  
  23.         make.right.equalTo(self.view);  
  24.         make.height.equalTo(@(CELL_WIDTH02 * 2));  
  25.     }];  
  26. }  


cell的数量可以通过宏定义ARRAY_COUNT来设置。

 

这里的Autolayout我使用Masonry进行自动布局,关于Masonry的使用可以参考《Autolayout第三方库Masonry的入门与实践》。

这里我设置UICollection的宽度为屏幕宽度,高度为2 * CELL_WIDTH02.

UICollectionViewFlowLayout是设置CollectionView内部cell的布局,必须进行设置。

 

(6)UICollectionViewDataSource代理中方法重写:

  1. #pragma mark - UiCollectionViewDataSource  
  2. - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section  
  3. {  
  4.     return self.collArr.count;  
  5. }  
  6.   
  7. - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath  
  8. {  
  9.     CustomCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath]; // 重用cell  
  10.     if (cell == nil)  
  11.     {  
  12.         // 当重用cell为空时,创建新的cell  
  13.         cell = [[CustomCollectionViewCell alloc] init];  
  14.     }  
  15.     cell.imageView.image = self.collArr[indexPath.row];  
  16.   
  17.     return cell;  
  18. }  


numberOfItemsInSection是设置section中cell的数量,返回数组数量即可,在这里其实就是ARRAY_COUNT宏定义。

 

cellForItemAtIndexPath是为cell进行赋值。

 

(7)UICollectionViewDelegateFlowLayout代理中方法的重写:

  1. #pragma mark - UICollectionViewDelegateFlowLayout  
  2.   
  3. // 该方法是设置cell的size  
  4. - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath  
  5. {  
  6.     return CGSizeMake(CELL_WIDTH02, CELL_WIDTH02);  
  7. }  
  8.   
  9. // 该方法是设置一个section的上左下右边距  
  10. - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section  
  11. {  
  12.     // 注意,这里默认会在top 有+64的边距,因为状态栏+导航栏是64.  
  13.     // 因为我们常常把[[UIScreen mainScreen] bounds]作为CollectionView的区域,所以苹果API就默认给了+64的EdgeInsets,这里其实是一个坑,一定要注意。  
  14.     // 这里我暂时不用这个边距,所以top减去64  
  15.     // 所以这是就要考虑你是把Collection从屏幕左上角(0,0)开始放还是(0,64)开始放。  
  16.     return UIEdgeInsetsMake(-64, 0, 0, 0);  
  17. }  
  18.   
  19. // 两个cell之间的最小间距,是由API自动计算的,只有当间距小于该值时,cell会进行换行  
  20. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section  
  21. {  
  22.     return MINIMUM_ITEM_SPACE;  
  23. }  
  24.   
  25. // 两行之间的最小间距  
  26. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section  
  27. {  
  28.     return MINIMUM_LINE_SPACE;  
  29. }  


 

 

-- 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效果如下:

  1. // 两个cell之间的最小间距,是由API自动计算的,只有当间距小于该值时,cell会进行换行  
  2. - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section  
  3. {  
  4.     return 14.0f;  
  5. }  


 

可以看到一行的cell只有3个了。因为当一行cell=4的时候,间距13.33小于了我设置的最小间距14,所以系统不得不一行减少一个cell来满足这个最小间距的要求。现在计算一下间距:(320 - 3 * 70) /2 = 55 > 14最小间距,满足条件, 通过截图来进行验证:

实际证明计算仍旧是正确的。

 

      现在大家应该明白了minimumInteritemSpacingForSectionAtIndex这个方法的含义了吧,也知道了如何去调整cell的间距。所以综上所述一下,对于控制CollectionView中一行显示的cell数量和cell间距,只能通过cell的宽高和minimumInteritemSpacingForSectionAtIndex设置最小间距来完成,而不是通过代码手动设置的,计算任务就交给系统吧。

转载于:https://my.oschina.net/daniels/blog/795075

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值