UICollectionView是与UITableView相似的控件,不过它的布局更加*。
与UITableView的不同
tableView | collectionView | |
---|---|---|
初始化 | 需要指定布局style。 | 需要指定一个布局类。 |
子视图布局 | 一行代表一个cell,布局只需要考虑行高。 | 无视行列限制,一个item对应一个cell,由布局类来指定视图位置。 |
重用机制 | 针对cell,且非强制,某种cell也可以在非注册下使用。 | 针对所有子视图,且强制,必须注册才能使用。 |
重用机制调用初始化方法 | 在tableView的重用机制里,对于注册了的类,会在需要创建该类的对象的时候自动调用该类的initWithStyle:reuseIdentifier:方法。 | 在collectionView的重用机制里,对于注册了的类,会在需要该类的对象的时候自动调用该类的initWithFrame:方法。 |
以上摘自:/ohyeahhhh/article/details/51222590
UICollectionViewLayout与UICollectionViewFlowLayout简介
UICollectionViewLayout是一个layout对象,UICollectionView几乎所有的显示效果都由UICollectionViewLayout负责。
UICollectionViewFlowLayout是继承自UICollectionViewLayout的,是官方实现的流水布局效果,是一种非常经典的布局效果,应该也是我们最常用的。
UICollectionViewFlowLayout的一些基本属性:
- itemSize 每个item的大小;
- minimumLineSpacing 每行最小间距;
- minimumInteritemSpacing 每列最小间距;
- sectionInset 每个section的边距;
- scrollDirection 元素滚动方向。
流水式布局:
流布局是苹果预先定义的布局,这种布局就好比流水一样,将一个个cell按顺序排列。出来的效果跟网格差不多。
简单使用UICollectionView布置简单九宫格视图
- 初始化UICollecctionViewFlowLayout与UICollectionView。设置UICollectionView的delegate和dataSource。注意cell必须被注册才能使用。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
flowLayout.itemSize = CGSizeMake(100, 100);
flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
flowLayout.minimumLineSpacing = 20;
flowLayout.minimumInteritemSpacing = 20;
flowLayout.sectionInset = UIEdgeInsetsMake(20, 20, 20, 20);
self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:flowLayout];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
[self.view addSubview:self.collectionView];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 完成协议方法(基本与tableView一致)。
- (NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 10;
}
- (UICollectionViewCell*) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
UICollectionViewCell* cell = [self.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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
效果:
自定义layout实现瀑布流布局
先放效果图:
UICollectionViewLayoutAttributes
首先介绍UICollectionViewLayoutAttributes类,它保存了每一个cell的大小位置等属性,每一个cell都有一个对应的UICollectionViewLayoutAttributes。UICollectionViewLayout正是通过它保存的信息进行布局。
涉及的方法
-(void)prepareLayout;
-(CGSize)collectionViewContentSize;
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
-
-(void)prepareLayout;
在该方法用于计算各cell的位置和大小,并把它们封装成一个UICollectionViewLayoutAttributes。 -
-(CGSize)collectionViewContentSize;
该方法返回ContentView的大小。 -
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
该方法返回包含所有子视图的UICollectionViewLayoutAttributes对象的数组。
具体步骤
- 创建一个继承自UICollectionViewFlowLayout的类,添加一个itemCount。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MyLayout : UICollectionViewFlowLayout
@property (nonatomic, assign) int itemCount;
@end
NS_ASSUME_NONNULL_END
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 在实现部分添加一个NSMutableArray的成员变量,用于储存所有子视图的UICollectionViewLayoutAttributes对象。
- 重写
-(void)prepareLayout;
在该方法内计算每个cell的位置和大小,并把它们封装成一个UICollectionViewLayoutAttributes对象,添加到专门保存UICollectionViewLayoutAttributes对象的成员变量中。下面是代码:
- (void) prepareLayout {
attributeArray = [[NSMutableArray alloc] init];
[super prepareLayout];
// 计算每个item的宽度
float WIDTH = ([UIScreen mainScreen].bounds.size.width - self.minimumInteritemSpacing - self.sectionInset.left - self.sectionInset.right) / 2;
// 这个数组用于储存当前左右两列瀑布流的长度,保证新的item添加在短的一边下面。
CGFloat colHight[2] = {0};
// 循环计算每个item的位置大小
for (int i = 0; i < self.itemCount; i++) {
// 获取index
NSIndexPath* index = [NSIndexPath indexPathForItem:i inSection:0];
// 根据index创建attributes对象
UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
// 随机一个高度,在40-190之间
CGFloat hight = arc4random()%150 + 40;
// 标记最短的列
int width = 0;
// 将新的item的高度加到最短的列下
if (colHight[0] < colHight[1]) {
colHight[0] = colHight[0] + hight + self.minimumLineSpacing;
width = 0;
} else {
colHight[1] = colHight[1] + hight + self.minimumLineSpacing;
width = 1;
}
// 将改item的位置和大小封装成UICollectionViewLayoutAttributes对象
attributes.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + WIDTH) * width, colHight[width] - hight - self.minimumLineSpacing, WIDTH, hight);
// 保存UICollectionViewLayoutAttributes对象
[attributeArray addObject:attributes];
}
// 通过预设itemSize的大小保证滑动范围的正确(取一个高度上的平均值),也可以通过重写`-(CGSize)collectionViewContentSize; `方法完成
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[1] - self.sectionInset.top) * 2 / self.itemCount - self.minimumLineSpacing));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 重写
-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect;
方法返回包含所有子视图的UICollectionViewLayoutAttributes对象的数组。
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
return attributeArray;
}
- 1
- 2
- 3
- 初始化CollectionView。
- (void) getMyLayout {
MyLayout* layout = [[MyLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.itemCount = 100;
layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
self.collectionView.delegate = self;
self.collectionView.dataSource = self;
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
[self.view addSubview:self.collectionView];
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 完成协议方法。
- (NSInteger) numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return 100;
}
- (UICollectionViewCell*) collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath {
UICollectionViewCell* cell = [self.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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14