文章目录
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的相关属性
- 动态设置每个Item的尺寸大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
- 动态设置每个分区的EdgeInsets
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
- 动态设置每行的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
- 动态设置每列的间距大小
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
- 动态设置某个分区头视图大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
- 动态设置某个分区尾视图大小
- (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的布局配置,最后添加到上面数组里。
简而言之,就是下面两个步骤:
- 重写prepareLayout方法,将每一个item的布局配置都添加进去,然后添加到一个数组中。
- 重写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
然后我们需要去重写这个类的三个方法:
- 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];
}
}
- 重写collectionViewContentSize方法来设置内容的区域大小
-(CGSize)collectionViewContentSize {
return self.collectionView.frame.size;
}
- layoutAttributesForElementsInRect方法来返回我们的布局信息数组
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return attributeArray;
}
效果:
学习和摘抄于大佬的博客:大佬博客传送门。