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文件如下:

#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的宽高。

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

UICollectionViewDelegate,

UICollectionViewDataSource,

UICollectionViewDelegateFlowLayout

(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设置最小间距来完成,而不是通过代码手动设置的,计算任务就交给系统吧。

时间: 2024-10-02 07:19:53

iOS开发实战——CollectionView中cell的间距设置的相关文章

iOS开发实战——CollectionView点击事件与键盘隐藏结合案例(二)

我在前一篇博客中<iOS开发实战--CollectionView点击事件与键盘隐藏结合案例>详细实现了CollectionView与键盘组合操作中出现的多种情况,并解决了交互体验上的一些问题.在实际项目中也的确可以采用这种方法来操作.但是问题来了,原来的界面我们是使用UIView来操作的,也就是界面是不可滚动的.然而更为常见的场景是一个ScrollView,界面可以进行上下滚动.所以,这篇博客主要是对前一个案例进行优化.还有一个问题是,在自动布局Masonry结合ScrollView中,会碰到

iOS开发——实战OC篇&amp;环境搭建之Xib(玩转UINavigationController与UITabBarController)

iOS开发——实战OC篇&环境搭建之Xib(玩转UINavigationController与UITabBarController) 前面我们介绍了StoryBoard这个新技术,和纯技术编程的代码创建界面,本篇我们将介绍一个老的技术,但是在很多的公司或者库里面还是使用这个技术,既然如此它肯定有他的好处,至于好处这里我就不一一介绍了.在Xcode5之前是只能使用Xib或者代码的,而代码又对于很多初学者来说算是一个难题.毕竟不知道怎么下手.所以我就总结了一下这段时间自己编写程序的一个实例来说明怎么

iOS开发——实战OC篇&amp;环境搭建之纯代码(玩转UINavigationController与UITabBarController)

iOS开发——实战OC篇&环境搭建之纯代码(玩转UINavigationController与UITabBarController) 这里我们就直接上实例: 一:新建一个项目singleView Controller,命名未iCocos 二:由于我们使用的纯代码实现的,所以删除其中的StoryBoard和Viewtroller的两个文件 三:新建一个继承自TabBar Controller的类,我们命名问iCocos ViewController 三:在Appdelegate的实现文件中导入刚刚

Xamarin iOS开发实战上册-----2.2.2 使用代码添加视图

Xamarin iOS开发实战上册-----2.2.2  使用代码添加视图 如果开发者想要使用代码为主视图添加视图,该怎么办呢.以下将为开发者解决这一问题.要使用代码为主视图添加视图需要实现3个步骤. 1.实例化视图对象 每一个视图都是一个特定的类.在C#中,经常会说,类是一个抽象的概念,而非具体的事物,所以要将类进行实例化.实例化一个视图对象的具体语法如下: 视图类 对象名=new 视图类(); 以我们接触的第一个视图View为例,它的实例化对象如下: UIView vv=new UIView

Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序

Xamarin iOS开发实战第1章使用C#编写第一个iOS应用程序 C#原本是用来编写Windows以及Windows Phone的应用程序.自从Xamarin问世后,C#的作用就发生了很大的变化.它不仅可以编写关于Windows以及Windowsx Phone的应用程序,还可以编写iOS.Android的应用程序.本章将讲解如何使用C#编写一个简单的iOS应用程序.本文选自<Xamarin iOS开发实战> 1.1初识Xamarin Xamarin是一个跨平台的开发框架.Xamarin的产

iOS 开发 关于应用中使用拨打电话那点事

一.利用openURL(tel) 特点: 直接拨打, 不弹出提示. 并且, 拨打完以后, 留在通讯录中, 不返回到原来的应用. - (void)callPhone:(NSString *)phoneNumber {     //phoneNumber = "18369......"     NSMutableString * str=[[NSMutableString alloc] initWithFormat:@"tel:%@",phoneNumber];    

Xamarin iOS开发实战1.1.3Xamarin版本

Xamarin iOS开发实战1.1.3Xamarin版本 Xamarin提供了免费版和付费版.免费版本包含Xamarin Studio服务.付费版本分为普通版299美元/年.商业版999美元/年和企业版1899美元/年.开发者可以根据自身需要进行购买,如图1.1所示.本文选自Xamarin iOS开发实战大学霸 图1.1  Xamarin各个版本 注意:针对学生及研究人员,Xamarin提供以99美元/年的价格购买商业版授权. 1.1.4  工具需求 要使用C#编写iOS应用程序,需要使用到3

iOS开发UITableViewCell的选中时的颜色设置

1.系统默认的颜色设置 //无色 cell.selectionStyle = UITableViewCellSelectionStyleNone; //蓝色 cell.selectionStyle = UITableViewCellSelectionStyleBlue; //灰色 cell.selectionStyle = UITableViewCellSelectionStyleGray; 2.自定义颜色和背景设置 改变UITableViewCell选中时背景色: UIColor *color

[IOS开发教程] iOS如何固定UITableView中cell.imageView.image的图片大小

凡是进行ios开发的,基本上都会遇到要展示列表,或者即使不是标准列表,但由于数量不固定,也需要如同列表一样从上往下显示.加载的情况.这些,都绕不过对UITableView的使用. 在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似于微信.QQ.新浪微博等软件基本上随处都是UITableView.当然它的广泛使用自然离不开它强大的功能. 我们经常在开发过程中会用到默认UITableView的cell.imageView.image,如果图