collectionView
来源:互联网 发布:数据迁移整体解决方案 编辑:程序博客网 时间:2024/05/01 14:18
##collectionView图片无限滚动实现的思想
* collectionView只有3个Cell。每次滚动结束后,都让collectionView以非动画方式,滚回到第一个Cell(Cell从序数0开始)上,以保证每次拖拽滚动结束后,collectionView依然可以滚动
* 通过监听collectionView的滚动方向对应修改图片的全局索引值,每次collectionView自动滚回到第一个Cell,在collectionView创建Cell时,将对应的内容设置给collectionView的第一个Cell
* collectionView刷新Cell前关闭动画,刷新后重新开启动画
```objc
// 监听collectionView的滚动事件
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
// 根据滚动结束后,collectionView的内容偏移值,计算出要显示的图片索引的变化
CGFloat offset = self.collectionView.contentOffset.x;
NSInteger offsetPage = (offset / self.view.frame.size.width) - 1;
// 未翻页,什么都不做
if (!offsetPage) {
return;
}
// +总页数对总页数取模,保证不越界
self.imageIndex = (self.imageIndex + offsetPage + self.images.count) % self.images.count;
// 每次滚动结束后,重新让collectionView滚动到中间Cell上
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:0];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
// 必须要刷新Cell。
// 每次新Cell出现时,都调用了代理返回Cell的方法,刷新了数据。为什么会出现Cell显示图片错误的问题?
// 这个显示bug是由于collectionView多线程任务执行完成顺序先后造成的
[UIView setAnimationsEnabled:NO];
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
[UIView setAnimationsEnabled:YES];
}
```
###-viewDidAppear:
程序启动viewDidLoad方法执行完时,collectionView的代理方法还没有调用。此时设置collectionView的Cell的相关属性和数据,是无意义的。必须要等到View被显示之后(所有的相关方法都调用执行完毕),才可以设置collectionView的Cell的相关参数和属性
---
##瀑布流布局实现思路及具体步骤
###思路
1. 定宽不定高:列宽相等,每一个item的高度由内容决定。
2. 每列下面一个Cell的Y轴起点,由上一个Cell的底部决定
3. collectionView的内容尺寸,由Cell最大的Y值决定
###实现步骤
1. 为collectionView注册xib模板的Cell,设置Cell的子控件及约束
2. 实现collectionView的数据源协议方法,布局Cell
3. 自定义继承自UICollectionViewFlowLayout类的子类,重写父类的某些方法,手动计算每一个Cell对应的属性
4. 设置collectionView的flowLayout参数关联新建的自定义类
5. storyboard中给collectionView添加footerView,并关联自定义类,实现数据源方法中返回页脚View方法
6. 实现新增数据并刷新的方法,当FooterView 被创建后,自动执行并刷新数据
$$控制器类相关属性$$
```objc
@property (nonatomic, strong) NSMutableArray *goods;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet ASWaterFlowLayout *flowLayout;
@property (nonatomic, weak) ASFooterView *footerView;
```
###自定义flowLayout类计算每个Cell的frame属性
$$属性$$
```objc
@property (nonatomic, assign) NSInteger columnCount; // 排布列数
@property (nonatomic, weak) id<ASWaterFlowLayoutDelegate> delegate; // 获取Cell高度的代理对象
```
$$代理协议$$
```objc
@protocol ASWaterFlowLayoutDelegate <NSObject>
// 根据Cell序数及宽度,获取Cell高度
- (CGFloat)waterFlowLayoutGetCellHeight:(ASWaterFlowLayout *)waterFlowLayout
withIndex:(NSInteger)index andCellWidth:(CGFloat)cellWidth;
@end
```
$$延展属性$$
```objc
@property (nonatomic, strong) NSMutableArray *arrayAttrs; // 自定义保存每个Cell属性的数组
@property (nonatomic, strong) NSMutableDictionary *dictMaxY; // 自定义保存每行当前Cell总高度的字典
@property (nonatomic, copy) NSString *keyMaxY; // 字典中最大Y值对应的key
@property (nonatomic, copy) NSString *keyMinY; // 字典中最小Y值对应的key
```
$$字典和数组的懒加载$$
```objc
- (NSMutableArray *)arrayAttrs{
if (_arrayAttrs == nil) _arrayAttrs = [NSMutableArray array];
return _arrayAttrs;
}
- (NSMutableDictionary *)dictMaxY{
if (!_dictMaxY) _dictMaxY = [NSMutableDictionary dictionary];
return _dictMaxY;
}
```
$$重写prepareLayout方法$$
```objc
- (void)prepareLayout{
[super prepareLayout];
// 初始化字典,让字典拥有三组键值
for (int i=0; i<self.columnCount; i++) {
NSString *key = [NSString stringWithFormat:@"%d", i];
self.dictMaxY[key] = @"0";
}
// 每次刷新数据准备布局collectionView时,清除所有Cell的layout属性
[self.arrayAttrs removeAllObjects];
// 计算所有Cell的属性
// 获取collectionView第0组的总Cell数
NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
for (int i=0; i<cellCount; i++) {
// 获取当前Cell所在的坐标
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
// 获取对应Cell的属性数据
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:indexPath];
// 将当前Cell的属性信息添加到Cell属性数组中
[self.arrayAttrs addObject:attr];
}
// 计算footerView的属性
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
UICollectionViewLayoutAttributes *attrFooter = [UICollectionViewLayoutAttributes
layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];
// 根据内容最大Y值,设置footerView的属性
NSString *maxHeight = self.keyMaxY;
attrFooter.frame = CGRectMake(0, [self.dictMaxY[maxHeight] floatValue],
self.collectionView.frame.size.width, self.footerReferenceSize.height);
[self.arrayAttrs addObject:attrFooter];
}
```
**注:**属性数组中,可以重复为指定indexPath的Cell添加多个不同的属性实例,最后布局时,以最后一个被添加进数组的属性实例为准。但是,不可以添加多于1个footerView的属性实例!新添加进的footerView的属性实例不会在布局时替换旧值,而是直接冲突报错!
![](Snip20160117_2.png)
$$计算每个item的frame并返回$$
```objc
// 计算Cell的属性。控制器每布局一个Cell,都会来调用一次这个方法
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
// 计算每一个Cell的frame
CGFloat contentW = self.collectionView.frame.size.width - self.sectionInset.left -
self.sectionInset.right - self.minimumInteritemSpacing * (self.columnCount - 1);
// 计算每个Cell的宽,代理获取对应的高
CGFloat cellW = contentW / self.columnCount;
CGFloat cellH = [self.delegate waterFlowLayoutGetCellHeight:self withIndex:indexPath.item andCellWidth:cellW];
// 将Cell布局在Y值最小的列
NSInteger col = [self.keyMinY integerValue];
CGFloat cellX = self.sectionInset.left + col * (cellW + self.minimumInteritemSpacing);
NSString *keyCol = [NSString stringWithFormat:@"%ld", col];
CGFloat cellY = [self.dictMaxY[keyCol] floatValue];
// 更新字典中布局Cell所在列的最大高度
CGFloat maxY = cellY + cellH + self.minimumInteritemSpacing;
self.dictMaxY[keyCol] = [NSString stringWithFormat:@"%f", maxY];
// 修改对应Cell在数组中保存的属性值
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attr.frame = CGRectMake(cellX, cellY, cellW, cellH);
// 返回Cell的属性值
return attr;
}
```
$$返回可视范围内所有Cell的布局属性泛型数组$$
```objc
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.arrayAttrs;
}
```
$$返回collectionView的contentSize$$
```objc
- (CGSize)collectionViewContentSize{
return CGSizeMake(0, [self.dictMaxY[self.keyMaxY] floatValue] + self.footerReferenceSize.height);
}
```
$$获取字典中最大/小Y值对应的key$$
```objc
- (NSString *)keyMaxY{
// 假定最大的key为第0组键值的key
__block NSString *keyMaxY = @"0";
// block方式遍历字典,比较Y值,获取最大的key
[self.dictMaxY enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull value, BOOL * _Nonnull stop) {
if ([self.dictMaxY[keyMaxY] floatValue] < [value floatValue]) {
keyMaxY = key;
}
}];
return keyMaxY;
}
```
###刷新数据功能模块
$$更新数据数组$$
类似于懒加载,根据新数据plist文件,构建模型数组,数组中的模型元素添加到原数据数组中
$$滚出FooterView之后的刷新数据事件$$
```objc
// 监听collectionView的滚动位置。当footerView被显示出来后,执行这个方法中的内容
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
// FooterView没有出现或者指示器还在旋转时,返回
if (!self.footerView || [self.footerView.activityIndicator isAnimating]) return;
[self.footerView.activityIndicator startAnimating]; // 指示器开始动画
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self reloadData]; // 更新数据数组
[self.collectionView reloadData]; // collectionView刷新数据
[self.footerView.activityIndicator stopAnimating]; // 指示器停止动画
self.footerView = nil; // 释放FooterView
});
}
```
* collectionView只有3个Cell。每次滚动结束后,都让collectionView以非动画方式,滚回到第一个Cell(Cell从序数0开始)上,以保证每次拖拽滚动结束后,collectionView依然可以滚动
* 通过监听collectionView的滚动方向对应修改图片的全局索引值,每次collectionView自动滚回到第一个Cell,在collectionView创建Cell时,将对应的内容设置给collectionView的第一个Cell
* collectionView刷新Cell前关闭动画,刷新后重新开启动画
```objc
// 监听collectionView的滚动事件
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
// 根据滚动结束后,collectionView的内容偏移值,计算出要显示的图片索引的变化
CGFloat offset = self.collectionView.contentOffset.x;
NSInteger offsetPage = (offset / self.view.frame.size.width) - 1;
// 未翻页,什么都不做
if (!offsetPage) {
return;
}
// +总页数对总页数取模,保证不越界
self.imageIndex = (self.imageIndex + offsetPage + self.images.count) % self.images.count;
// 每次滚动结束后,重新让collectionView滚动到中间Cell上
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:0];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
// 必须要刷新Cell。
// 每次新Cell出现时,都调用了代理返回Cell的方法,刷新了数据。为什么会出现Cell显示图片错误的问题?
// 这个显示bug是由于collectionView多线程任务执行完成顺序先后造成的
[UIView setAnimationsEnabled:NO];
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
[UIView setAnimationsEnabled:YES];
}
```
###-viewDidAppear:
程序启动viewDidLoad方法执行完时,collectionView的代理方法还没有调用。此时设置collectionView的Cell的相关属性和数据,是无意义的。必须要等到View被显示之后(所有的相关方法都调用执行完毕),才可以设置collectionView的Cell的相关参数和属性
---
##瀑布流布局实现思路及具体步骤
###思路
1. 定宽不定高:列宽相等,每一个item的高度由内容决定。
2. 每列下面一个Cell的Y轴起点,由上一个Cell的底部决定
3. collectionView的内容尺寸,由Cell最大的Y值决定
###实现步骤
1. 为collectionView注册xib模板的Cell,设置Cell的子控件及约束
2. 实现collectionView的数据源协议方法,布局Cell
3. 自定义继承自UICollectionViewFlowLayout类的子类,重写父类的某些方法,手动计算每一个Cell对应的属性
4. 设置collectionView的flowLayout参数关联新建的自定义类
5. storyboard中给collectionView添加footerView,并关联自定义类,实现数据源方法中返回页脚View方法
6. 实现新增数据并刷新的方法,当FooterView 被创建后,自动执行并刷新数据
$$控制器类相关属性$$
```objc
@property (nonatomic, strong) NSMutableArray *goods;
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet ASWaterFlowLayout *flowLayout;
@property (nonatomic, weak) ASFooterView *footerView;
```
###自定义flowLayout类计算每个Cell的frame属性
$$属性$$
```objc
@property (nonatomic, assign) NSInteger columnCount; // 排布列数
@property (nonatomic, weak) id<ASWaterFlowLayoutDelegate> delegate; // 获取Cell高度的代理对象
```
$$代理协议$$
```objc
@protocol ASWaterFlowLayoutDelegate <NSObject>
// 根据Cell序数及宽度,获取Cell高度
- (CGFloat)waterFlowLayoutGetCellHeight:(ASWaterFlowLayout *)waterFlowLayout
withIndex:(NSInteger)index andCellWidth:(CGFloat)cellWidth;
@end
```
$$延展属性$$
```objc
@property (nonatomic, strong) NSMutableArray *arrayAttrs; // 自定义保存每个Cell属性的数组
@property (nonatomic, strong) NSMutableDictionary *dictMaxY; // 自定义保存每行当前Cell总高度的字典
@property (nonatomic, copy) NSString *keyMaxY; // 字典中最大Y值对应的key
@property (nonatomic, copy) NSString *keyMinY; // 字典中最小Y值对应的key
```
$$字典和数组的懒加载$$
```objc
- (NSMutableArray *)arrayAttrs{
if (_arrayAttrs == nil) _arrayAttrs = [NSMutableArray array];
return _arrayAttrs;
}
- (NSMutableDictionary *)dictMaxY{
if (!_dictMaxY) _dictMaxY = [NSMutableDictionary dictionary];
return _dictMaxY;
}
```
$$重写prepareLayout方法$$
```objc
- (void)prepareLayout{
[super prepareLayout];
// 初始化字典,让字典拥有三组键值
for (int i=0; i<self.columnCount; i++) {
NSString *key = [NSString stringWithFormat:@"%d", i];
self.dictMaxY[key] = @"0";
}
// 每次刷新数据准备布局collectionView时,清除所有Cell的layout属性
[self.arrayAttrs removeAllObjects];
// 计算所有Cell的属性
// 获取collectionView第0组的总Cell数
NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
for (int i=0; i<cellCount; i++) {
// 获取当前Cell所在的坐标
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
// 获取对应Cell的属性数据
UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:indexPath];
// 将当前Cell的属性信息添加到Cell属性数组中
[self.arrayAttrs addObject:attr];
}
// 计算footerView的属性
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
UICollectionViewLayoutAttributes *attrFooter = [UICollectionViewLayoutAttributes
layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath];
// 根据内容最大Y值,设置footerView的属性
NSString *maxHeight = self.keyMaxY;
attrFooter.frame = CGRectMake(0, [self.dictMaxY[maxHeight] floatValue],
self.collectionView.frame.size.width, self.footerReferenceSize.height);
[self.arrayAttrs addObject:attrFooter];
}
```
**注:**属性数组中,可以重复为指定indexPath的Cell添加多个不同的属性实例,最后布局时,以最后一个被添加进数组的属性实例为准。但是,不可以添加多于1个footerView的属性实例!新添加进的footerView的属性实例不会在布局时替换旧值,而是直接冲突报错!
![](Snip20160117_2.png)
$$计算每个item的frame并返回$$
```objc
// 计算Cell的属性。控制器每布局一个Cell,都会来调用一次这个方法
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
// 计算每一个Cell的frame
CGFloat contentW = self.collectionView.frame.size.width - self.sectionInset.left -
self.sectionInset.right - self.minimumInteritemSpacing * (self.columnCount - 1);
// 计算每个Cell的宽,代理获取对应的高
CGFloat cellW = contentW / self.columnCount;
CGFloat cellH = [self.delegate waterFlowLayoutGetCellHeight:self withIndex:indexPath.item andCellWidth:cellW];
// 将Cell布局在Y值最小的列
NSInteger col = [self.keyMinY integerValue];
CGFloat cellX = self.sectionInset.left + col * (cellW + self.minimumInteritemSpacing);
NSString *keyCol = [NSString stringWithFormat:@"%ld", col];
CGFloat cellY = [self.dictMaxY[keyCol] floatValue];
// 更新字典中布局Cell所在列的最大高度
CGFloat maxY = cellY + cellH + self.minimumInteritemSpacing;
self.dictMaxY[keyCol] = [NSString stringWithFormat:@"%f", maxY];
// 修改对应Cell在数组中保存的属性值
UICollectionViewLayoutAttributes *attr = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
attr.frame = CGRectMake(cellX, cellY, cellW, cellH);
// 返回Cell的属性值
return attr;
}
```
$$返回可视范围内所有Cell的布局属性泛型数组$$
```objc
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return self.arrayAttrs;
}
```
$$返回collectionView的contentSize$$
```objc
- (CGSize)collectionViewContentSize{
return CGSizeMake(0, [self.dictMaxY[self.keyMaxY] floatValue] + self.footerReferenceSize.height);
}
```
$$获取字典中最大/小Y值对应的key$$
```objc
- (NSString *)keyMaxY{
// 假定最大的key为第0组键值的key
__block NSString *keyMaxY = @"0";
// block方式遍历字典,比较Y值,获取最大的key
[self.dictMaxY enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull value, BOOL * _Nonnull stop) {
if ([self.dictMaxY[keyMaxY] floatValue] < [value floatValue]) {
keyMaxY = key;
}
}];
return keyMaxY;
}
```
###刷新数据功能模块
$$更新数据数组$$
类似于懒加载,根据新数据plist文件,构建模型数组,数组中的模型元素添加到原数据数组中
$$滚出FooterView之后的刷新数据事件$$
```objc
// 监听collectionView的滚动位置。当footerView被显示出来后,执行这个方法中的内容
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
// FooterView没有出现或者指示器还在旋转时,返回
if (!self.footerView || [self.footerView.activityIndicator isAnimating]) return;
[self.footerView.activityIndicator startAnimating]; // 指示器开始动画
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self reloadData]; // 更新数据数组
[self.collectionView reloadData]; // collectionView刷新数据
[self.footerView.activityIndicator stopAnimating]; // 指示器停止动画
self.footerView = nil; // 释放FooterView
});
}
```
0 0
- CollectionView
- collectionView
- collectionView
- collectionView
- CollectionView
- CollectionView
- collectionView
- collectionView
- CollectionView
- collectionView
- collectionview
- Customised collectionView
- collectionView学习
- iOS CollectionView
- iOS---collectionView
- collectionView 小结
- ios collectionView
- CollectionView视图
- 斐波那契数列
- 中文分词的条件随机场模型(CRF)介绍
- malloc函数,大大的详解
- 如果ROOT 用户忘记密码如何实现登陆呢
- 小狗钱钱
- collectionView
- 数据导出到Excel(或Word)源代码大全
- class has no zero argument constructor
- 第四次其他
- Abobe Flash cs6 和 Abobe Flash Builder4.6 破解和许可证过期
- Flume 抓取日志文件存入MySQL中
- php中eval函数笔记
- JAVA - 1、Win10 Eclipe Java 环境配置及 代码自动补全 设置
- UICollectionView