【iOS开发】—— UICollectionView的介绍

2 篇文章 0 订阅
本文介绍了如何使用UICollectionView实现各种布局,包括静态的九宫格布局、动态设置布局属性以及自定义FlowLayout实现瀑布流布局。通过实例代码详细展示了设置每个item的大小、方向、间距等属性,并探讨了自定义布局的实现原理和步骤,最终实现了一个动态的、不定高的瀑布流布局。
摘要由CSDN通过智能技术生成

UICollectionView简介

UICollectionView是iOS6.0之后一个新的UI控件,和UITableView有些相似,其中很多方法都很相似,在我看来,UICollectionView是对UITableView的强化。
这次用例子去学习一下它的用法。

设置静态的布局

实现简单的九宫格类布局

将下面的代码写在viewDidLoad中

UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    //定义每个cell的大小
    flowLayout.itemSize = CGSizeMake((self.view.frame.size.width - 80) / 3, 100);
    //定义布局方向
    flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
    //定义每个cell纵向的间距
    flowLayout.minimumLineSpacing = 20;
    //定义每个cell的横向间距
    flowLayout.minimumInteritemSpacing = 20;
    //定义每个cell到容器边缘的距离
    flowLayout.sectionInset = UIEdgeInsetsMake(20, 20, 20, 20);
    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:flowLayout];
    //注册cell
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
    //设置代理
    self.collectionView.delegate = self;
    //设置数据源
    self.collectionView.dataSource = self;
    [self.view addSubview:self.collectionView];

注:UICollectionView一定要注册一个cell,像下面这样:

[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@“cell”];

原因可能是:它不像UITableView那样:当复用池中没有可用的cell时,可以返回nil,然后临时创建;UICollectionView只能从复用池中获取cell,如果用别的方法,就会崩溃。

然后实现代理方法:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 9;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    return cell;
}

就像上面的(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath这个方法里面的:

UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];

不可以写为

UICollectionViewCell* cell =  [UICollectionViewCell alloc] init];

实现效果:
在这里插入图片描述

注意:
UICollectionView可以像UITableView滑动的,

typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
    UICollectionViewScrollDirectionVertical,   //垂直
    UICollectionViewScrollDirectionHorizontal。//横向
};

这样系统会在一行充满后进行第二行的排列,如果设置为水平布局,则会在一列充满后,进行第二列的布局,这种方式也被称为流式布局。

九宫格布局升级

上面我们写了一个简单九宫格的代码,所有的item都是一样大小的,但是有时候这样满足不了我们的需求,我们有时候可能也需要item不同大小。
在上面代码的基础上,删除控制item的大小的代码,再加入下面几行代码即可实现:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    if (indexPath.row % 2 == 0) {
        return CGSizeMake(50, 50);
    } else {
        return CGSizeMake(80, 80);
    }
}

效果:
在这里插入图片描述

以上这些效果的实现都是静态的,在加载前确定好的,那有没有动态地配置 layout的属性?
有!

设置动态的布局

动态的配置layout的相关属性

  1. 动态设置每个Item的尺寸大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
  1. 动态设置每个分区的EdgeInsets
 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
  1. 动态设置每行的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
  1. 动态设置每列的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
  1. 动态设置某个分区头视图大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section; 
  1. 动态设置某个分区尾视图大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

UICollectionView —— 自定义FlowLayout实现瀑布流布局

前面我们虽然利用UICollectionView实现了几种灵活的布局,但是都是局限于系统给我们的布局框架,下面我们自定义FlowLayout来实现item的不定高瀑布流似的布局,这种布局在我们的日常生活中也比较常见。

首先我们先创建一个继承于UICollectionViewFlowLayout的类MyLayout,然后在里面只写一个属性itemCount;
在这里插入图片描述

在介绍主要代码之前,我们得先清楚FlowLayout是什么东西?有什么用?
UICollectionViewFlowLayout是一个专门用来管理collectionView布局的类,因此UICollectionView在进行UI布局前,会通过这个类的对象获取相关的布局信息,FlowLayout类将这些布局信息全部存放在了一个数组中,==数组中存放的是UICollectionViewLayoutAttributes类,==这个类是对item布局的具体设置。
在UICollectionView布局前,会调用FlowLayout类的layoutAttributesForElementsInRect:方法来获取这个布局数组,因此我们可以重写这个方法,然后返回我们想要的布局方式的数组,另外FlowLayout在进行布局之前,还会调用prepareLayout方法,所以我们可以重写这个方法,然后设置每一个item的布局配置,最后添加到上面数组里。

简而言之,就是下面两个步骤:

  1. 重写prepareLayout方法,将每一个item的布局配置都添加进去,然后添加到一个数组中。
  2. 重写layoutAttributesForElementsInRect:方法,返回上面的数组。

示例代码:

//MyLayout.h
#import "MyLayout.h"

@implementation MyLayout {
    NSMutableArray* attributeArray;
}
- (void)prepareLayout {
    attributeArray = [[NSMutableArray alloc] init];
    [super prepareLayout];
    
    //设置为静态的两列
    //计算每个item的高度
    float WIDTH = ([UIScreen mainScreen].bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing) / 2;
    //定义数组保存每一列的高度
    //这个数组的主要作用是保存每一列的总高度,这样在布局时,我们可以始终将下一个Item放在最短的列下面
    CGFloat colHight[2] = {self.sectionInset.top, self.sectionInset.bottom};
    //itemCount是外界传进来的item的个数 遍历来设置每一个item的布局
    for (int i = 0; i < self.itemCount; i++) {
        //设置每个item的位置等相关属性
        NSIndexPath* index = [NSIndexPath indexPathForItem:i inSection:0];
        //创建一个布局属性类,通过NSIndexPath来创建
        UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
        //随机一个高度 在40——190之间
        CGFloat hight = arc4random()%150+40;
        //哪一列高度小 则放到那一列下面
        //标记最短的列
        int width = 0;
        if (colHight[0] < colHight[1]) {
            //将新的item高度加入到短的一列
            colHight[0] = colHight[0] + hight + self.minimumLineSpacing;
            width = 0;
        } else {
            colHight[1] = colHight[1] + hight + self.minimumLineSpacing;
            width = 1;
        }
        attributes.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + WIDTH) * width, colHight[width] - hight - self.minimumLineSpacing, WIDTH, hight);
        [attributeArray addObject:attributes];
    }
    
    if (colHight[0] > colHight[1]) {
        self.itemSize = CGSizeMake(WIDTH, (colHight[0] - self.sectionInset.top) * 2 / self.itemCount - self.minimumLineSpacing);
    } else {
        self.itemSize = CGSizeMake(WIDTH, (colHight[0] - self.sectionInset.top) * 2 / self.itemCount - self.minimumLineSpacing);
    }
}

//这个方法中返回我们的布局数组
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return attributeArray;
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    MyLayout* layout = [[MyLayout alloc]init];
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    layout.itemCount=100;
     UICollectionView * collect  = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
    collect.delegate=self;
    collect.dataSource=self;
    
    [collect registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellid"];
  
    [self.view addSubview:collect];
}
 
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 100;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell  = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellid" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    return cell;
}

实现效果:
在这里插入图片描述

UICollectionView实现圆环布局

先自定义一个layout类,继承于UICollectionViewLayout:

@interface MyLayout : UICollectionViewLayout
@property(nonatomic, assign)int itemCount;
@end

然后我们需要去重写这个类的三个方法:

  1. prepareLayout方法:
-(void)prepareLayout {
    [super prepareLayout];
    //获取item的个数
    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
    attributeArray = [[NSMutableArray alloc]init];
    //先设定大圆的半径 取长和宽最短的
    CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/2;
    //计算圆心位置
    CGPoint center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2);
    //设置每个item的大小为50*50 则半径为25
    for (int i=0; i<_itemCount; i++) {
        UICollectionViewLayoutAttributes * attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        //设置item大小
        attris.size = CGSizeMake(50, 50);
        //计算每个item的圆心位置
        /*
         .
         . .
         .   . r
         .     .
         .........
         */
        //计算每个item中心的坐标
        //算出的x y值还要减去item自身的半径大小
        float x = center.x+cosf(2*M_PI/_itemCount*i)*(radius-25);
        float y = center.y+sinf(2*M_PI/_itemCount*i)*(radius-25);
     
        attris.center = CGPointMake(x, y);
        [attributeArray addObject:attris];
    }
}

  1. 重写collectionViewContentSize方法来设置内容的区域大小
-(CGSize)collectionViewContentSize {
    return self.collectionView.frame.size;
}
  1. layoutAttributesForElementsInRect方法来返回我们的布局信息数组
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    return attributeArray;
}

效果:
在这里插入图片描述

学习和摘抄于大佬的博客:大佬博客传送门

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值