自定义流水布局(实现相册功能)
来源:互联网 发布:专升本网络教育要多久 编辑:程序博客网 时间:2024/06/05 05:25
自定义流水布局collectionViewFlowLayout
- 本文主要讲解如何进行自定义流水布局来实现一个相册功能,如下图所示:
一、
想要实现类似这样的相册功能有三种办法
1> 利用scrollView来写,添加三个imageView,然后实时监控scrollView的滚动,一旦有imageView离开就立刻放入缓存池中以便用来复用(这种方法属于比较麻烦的)
2> 利用tableView,但是tableView只支持竖直滚动,不支持水平,但是可以改变tableView的transfrom属性来实现(这种方法看起来有点不合常理)
3> 利用Apple自带的collectionView(iOS6之后就用得比较广泛了),collectionView是一种比较牛逼的控件,利用好它我们可以做出很多漂亮的界面,collectionView默认是垂直滚动,但是它也支持水平滚动,而且也有重用机制,我们只需要负责填充数据,控制cell的缩放罢了(因此collectionView是首选)
TableView和CollectionView的排布区别
1> tableView的排布是一行一行往下排布,而collectionView的排布完全取决于Laytout,怎样的Laytout布局决定了显示怎样的cell
2> 因此我们可以看出,想要让界面显示得更加好看(如瀑布流等),就得自定义布局了
接下来就开始实现功能啦!
二、实现如下图样式
首先实现能展现数据并且水平滚动
1> viewDidLoad方法中创建collectionView,并且注册cell
- (void)viewDidLoad{ [super viewDidLoad]; CGFloat w = self.view.bounds.size.width; CGRect collectionViewFrame = CGRectMake(0, 100, w, 200); // 创建collectionView UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:collectionViewFrame collectionViewLayout:[[UICollectionViewFlowLayout alloc] init]]; // 设置代理和数据源 collectionView.delegate = self; collectionView.dataSource = self; // 注册cell [collectionView registerNib:[UINib nibWithNibName:@"DSImageCell" bundle:nil] forCellWithReuseIdentifier:ID]; // 添加到控制器View中 [self.view addSubview:collectionView];}
2> 创建images数组模型,实现collectionView的数据源方法
@property (nonatomic, strong) NSMutableArray *images;
- (NSArray *)images{ if (_images== nil) { self.images = [NSMutableArray array]; for (int i = 1; i < 20; i++) { [self.images addObject:[NSString stringWithFormat:@"%d", i]]; } } return _images;}
/** * 每一组有多少个cell */- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return self.images.count;} /** * 第indexPath位置上显示什么样的cell */- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ DSImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath]; cell.image = self.images[indexPath.item]; return cell;}
注意:这里我用的是自定义cell(DSImageCell),并且用了xib来展示item,以下是DSImageCell的.h和.m文件
@interface DSImageCell : UICollectionViewCell @property (nonatomic, copy) NSString *image; @end
@interface DSImageCell () @property (weak, nonatomic) IBOutlet UIImageView *iconView; @end @implementation DSImageCell - (void)awakeFromNib { // 边框颜色 self.iconView.layer.borderColor = [UIColor whiteColor].CGColor; // 边框宽度 self.iconView.layer.borderWidth = 3; // 变宽圆角 self.iconView.layer.cornerRadius = 5; // 剪切边框超出范围 self.iconView.layer.masksToBounds = YES; } - (void)setImage:(NSString *)image { _image = [image copy]; self.iconView.image = [UIImage imageNamed:image]; } @end
因为在创建collectionView的时候在init方法中传入的collectionViewLayout是系统自带的UICollectionViewFlowLayout,所以默认是垂直滚动的,所以此时就得自定义流水布局了!
三.
自定义流水布局,创建一个DSLineLayout继承自UICollectionViewFlowLayout,在.m文件中实现如下需求:
1.cell的缩放2.停止滚动后cell居中
1> 设置滚动方向,cell的大小,cell之间的间隔,还有第一个cell和最后一个cell居中(注意:初始化一般在prepareLayout方法中进行,不能在init方法中,因为init方法中collectionView是没有尺寸的),而且每一个cell都有自己的UICollectionViewLayoutAttributes属性,该属性可以控制cell的位置,大小等
/** * 一些初始化的工作最好在这里实现 */ - (void)prepareLayout { [super prepareLayout]; // 水平方向 self.scrollDirection = UICollectionViewScrollDirectionHorizontal; // 设置item的宽高 self.itemSize = CGSizeMake(DSItemWH, DSItemWH); // 设置cell之间的间距 self.minimumLineSpacing = DSItemWH - 40; CGFloat inset = (self.collectionView.frame.size.width - DSItemWH) * 0.5; // 设置组头和组尾的inset(让第一个和最后一个cell显示在最中间) self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset); }
至于DSItemWH就是cell的默认宽高
static const CGFloat DSItemWH = 100;
这个时候就可以实现上面图片所展示的样式了,所以接下来就需要实现cell在滚动中的缩放(也就是需要时刻监听屏幕范围内cell,此时得重写两个方法)
第一个方法:判断是否要在collectionView的边界发生改变的时候重新布局cell,该方法默认返回NO,如果返回YES,内部会重新调用prepareLayout和layoutAttributesForElementsInRect方法获得所有cell的布局属性
/** * 只要边界改变的时候重新布局性 */ -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES;}
第二个方法:也就是刚才上面所说的layoutAttributesForElementsInRect方法,该方法可以在滚动中重新布局cell,然后返回rect范围的所有cell的UICollectionViewLayoutAttributes属性,所以想要实现滚动中控制cell的缩放就得在这个方法里面实现
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{ return nil;}
下面是实现的思路:首先取得屏幕可见的rect范围,然后从遍历所有cell的UICollectionViewLayoutAttributes,判断cell是否在屏幕内,然后根据cell的centerX和屏幕的centerX之间的距离来算出需要缩放的比例,然后赋值给cell的transfrom属性
/** * 选中该rect内的所有子控件 * * @param rect 所有item加起来的rect * * @return 所有item */- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{ // 0.屏幕的可见范围 CGRect visiableRect; visiableRect.size = self.collectionView.frame.size; visiableRect.origin = self.collectionView.contentOffset; // 1.取出所有item的UICollectionViewLayoutAttributes NSArray *attributeArray = [super layoutAttributesForElementsInRect:rect]; // 获取屏幕中点的X CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5; // 2.遍历所有的布局属性 for (UICollectionViewLayoutAttributes *attrs in attributeArray) { // 如果不在屏幕上,直接跳过(两个rect是否相交) if (!CGRectIntersectsRect(visiableRect, attrs.frame)) continue; // 获取item的中点X CGFloat itemCenterX = attrs.center.x; CGFloat scale = 1+ 0.8 * (1 - ABS(centerX - itemCenterX) / (self.collectionView.frame.size.width * 0.5)); attrs.transform = CGAffineTransformMakeScale(scale, scale); } return attributeArray;}
接下来,相册功能就差cell滚动停止后自动回到中点位置
在实现之前还得知道一个方法
/** * 用来设置collectionView停止滚动那一刻的位置 * * @param proposedContentOffset 原本collectionView停止滚动那一刻的位置 * @param velocity 滚动速度 * * @return 想要停止滚动的位置 */- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{ }
该方法设置collectionView停止滚动那一刻的位置, proposedContentOffset表示**原本**collectionView停止滚动那一刻的位置, velocity滚动速度,而返回值就是你想要设置的位置.接下来说下实现思路:
首先获取屏幕的centerX,然后获取滚动结束后还在屏幕里面所有cell的centerX,并且找出离屏幕中点X最近的cell,求出两个centerX之间的差值(不需要求绝对值,因为差值又可能为正/负),所以cell停下来的位置就是proposedContentOffset.x加上这段差值
// 1.获取屏幕中点的X CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5; // 2.计算屏幕滚动最后一刻的位置(lastRect大小和collectionView的frame一样) CGRect lastRect; lastRect.origin = proposedContentOffset; lastRect.size = self.collectionView.frame.size; // 3.取出lastRect范围内的item属性 NSArray *attributeArray = [super layoutAttributesForElementsInRect:lastRect]; // 4.遍历lastRect范围内的item属性 CGFloat adjustContentOffet = MAXFLOAT; for (UICollectionViewLayoutAttributes *attrs in attributeArray) { // 选出其中item的中点X和collectionView中点X的绝对值最小的item出来 if (ABS(attrs.center.x - centerX) < ABS(adjustContentOffet)) { adjustContentOffet = attrs.center.x - centerX; // 该差值可能为正/负 } } return CGPointMake(proposedContentOffset.x + adjustContentOffet, proposedContentOffset.y);
到此为止,整个相册功能就实现完毕了,以上代码基本上都在了,由于代码里面注释写得十分清楚,所以就不多以言语代替了,而且因为是系统自带的循环利用,所以用起来一点都不会卡!
- 自定义流水布局(实现相册功能)
- 自定义UICollectionViewFlowLayout实现相册功能
- UICollectionView自定义流水布局
- android 自定义view实现自动相册功能
- android自定义相册的功能实现
- 利用自定义的 RecyclerView 实现相册的滑动功能
- 流水布局UICollectionView初级的自定义
- 高级UI:自定义标签流水布局
- android实现图片相册功能
- imageswitcher+gallay实现相册功能
- iOS 相册备份功能实现
- Hexo+Github实现相册功能
- ionic 相册功能的实现
- 自定义布局子流布局实现标签功能
- 一个RecyclerView实现QQ空间相册布局
- jQuery实现仿QQ相册功能
- jquery插件实现qq相册功能
- Android相册功能技术实现细节
- C++模板类重载"<<"未定义错误
- java基础 16年2月23日
- CodeForces 593A 2Char(暴力枚举26个字母的组合)
- mysql 常用操作命令
- 一.项目介绍及django的MTV模式简介
- 自定义流水布局(实现相册功能)
- 2754: [SCOI2012]喵星球上的点名 AC自动机+map
- iOS基础概念
- Eclipse全键盘编码攻略之一——入门
- 深入理解Angular依赖注入
- 响应式布局基础二:设置viewport
- Windows 下 MySQL 5.7.9 安装教程
- 利用javascript实现从某年到当前年的下拉列表框
- 消息队列------Redis