iOS开发--横向流水布局实现
前言:因为时间缘故,很少进行通俗易懂的算法思路讲解,这里先展示动态图片效果,然后后面的内容我就直接上关键源码了。
效果展示图;
源码百度云盘下载链接: http://pan.baidu.com/s/1eQOOixc 密码: duu8
源码:
1 // PhotoCell.h
2 // 自定义流水布局
3 //
6 //
7
8 #import <UIKit/UIKit.h>
9
10 @interface PhotoCell : UICollectionViewCell
11
12 @property (nonatomic, strong) UIImage *image;
13
14 @end
15
16 ================两个文件的分水岭==================
17 //
18 // PhotoCell.m
19 // 自定义流水布局
20 //
21 // Created by xmg on 16/1/15.
22 // Copyright © 2016年 HeYang. All rights reserved.
23 //
24
25
26
27 #import "PhotoCell.h"
28
29 @interface PhotoCell ()
30 @property (weak, nonatomic) IBOutlet UIImageView *imageView;
31 @end
32
33 @implementation PhotoCell
34
35 - (void)awakeFromNib {
36 // Initialization code
37 }
38
39 - (void)setImage:(UIImage *)image
40 {
41 _image = image;
42
43 _imageView.image = image;
44
45 }
46
47 @end
1 // 2 // PhotoLayout.h 3 // 自定义流水布局 4 // 5 // Created by xmg on 16/1/15. 6 // Copyright © 2016年 HeYang. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 11 @interface PhotoLayout : UICollectionViewFlowLayout 12 13 @end 14 15 16 ==============两个文件的分水岭================ 17 18 // 19 // PhotoLayout.m 20 // 自定义流水布局 21 // 22 // Created by xmg on 16/1/15. 23 // Copyright © 2016年 HeYang. All rights reserved. 24 // 25 26 #import "PhotoLayout.h" 27 #define XMGScreenW [UIScreen mainScreen].bounds.size.width 28 @implementation PhotoLayout 29 30 // 复杂效果: 分析 -> 31 // cell离中心点距离越近(delta越小),就越大,越远,就越小 32 // 距离中心点 33 // 知道哪些cell需要缩放:显示出来的cell才需要进行布局 34 35 36 37 // 给定一个区域,就返回这个区域内所有cell布局 38 - (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect 39 { 40 // 1.获取显示区域,bounds 而不是整个滚动区域,这样设置的子控件就控制在显示区域范围内了。 41 CGRect visiableRect = self.collectionView.bounds; 42 43 // 2.获取显示区域内的cell的布局 44 NSArray *attributes = [super layoutAttributesForElementsInRect:visiableRect]; 45 46 47 48 // 3.遍历cell布局 49 CGFloat offsetX = self.collectionView.contentOffset.x; 50 for (UICollectionViewLayoutAttributes *attr in attributes) { 51 // 3.1 计算下距离中心点距离 52 CGFloat delta = fabs(attr.center.x - offsetX - XMGScreenW * 0.5); 53 // 1 ~ 0.75 54 CGFloat scale = 1 - delta / (XMGScreenW * 0.5) * 0.25;//1-(0~0.5) = 1~0.5 --> 1-(0~0.5)×0.25 = (1~0.75) 55 attr.transform = CGAffineTransformMakeScale(scale, scale); 56 } 57 58 return attributes; 59 } 60 61 // Invalidate:刷新 62 // 是否允许在拖动的时候刷新布局 63 // 谨慎使用,YES:只要一滚动就会布局 64 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 65 { 66 return YES; 67 } 68 69 // 确定最终显示位置 70 // 什么时候调用:手动拖动UICollectionView,当手指离开的时候,就会调用 71 // 作用:返回UICollectionView最终的偏移量 72 // proposedContentOffset:最终的偏移量 73 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity 74 { 75 // 定位:判断下哪个cell里中心点越近,就定位到中间 76 // 1.获取最终显示的区域 77 CGRect targetRect = CGRectMake(proposedContentOffset.x, 0, self.collectionView.bounds.size.width, MAXFLOAT); 78 79 // 2.获取最终显示cell的布局 80 NSArray *attributes = [super layoutAttributesForElementsInRect:targetRect]; 81 82 // 3.遍历所有cell的布局,判断下哪个离中心点最近 83 CGFloat minDelta = MAXFLOAT; 84 85 for (UICollectionViewLayoutAttributes *attr in attributes) { 86 // 3.1 计算下距离中心点距离 87 CGFloat delta = attr.center.x - proposedContentOffset.x - XMGScreenW * 0.5; 88 // 3.2 计算最小间距 89 if (fabs(delta) < fabs(minDelta)) { 90 minDelta = delta; 91 } 92 } 93 94 proposedContentOffset.x += minDelta; 95 96 if (proposedContentOffset.x < 0) { 97 proposedContentOffset.x = 0; 98 } 99 100 return proposedContentOffset; 101 } 102 103 @end
最后的关键代码:
1 // 2 // ViewController.m 3 // 自定义流水布局 4 // 5 // Created by xmg on 16/1/15. 6 // Copyright © 2016年 HeYang. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 #import "PhotoCell.h" 11 #import "PhotoLayout.h" 12 13 /* 14 // a , b , c a = b + c 15 // int d = (2,3,5); 16 17 // 高聚合,低耦合 18 int a = ({ 19 int b = 2; 20 int c = 3; 21 a = b + c; 22 20; 23 }); 24 */ 25 26 /* 27 UICollectionView注意点: 28 1.初始化必须要传入布局,(流水布局:九宫格布局) 29 2.UICollectionViewCell必须要注册 30 3.必须自定义cell 31 */ 32 33 34 #define XMGScreenW [UIScreen mainScreen].bounds.size.width 35 static NSString * const ID = @"cell"; 36 @interface ViewController ()<UICollectionViewDataSource> 37 38 @end 39 40 @implementation ViewController 41 42 // 思路:照片浏览布局:流水布局,在拖到的时候,在原来基础上重新计算下布局 -> 在原来功能上再添加功能,自定义流水布局 43 - (void)viewDidLoad { 44 [super viewDidLoad]; 45 // Do any additional setup after loading the view, typically from a nib. 46 47 // 创建流水布局 48 PhotoLayout *layout = ({ 49 layout = [[PhotoLayout alloc] init]; 50 // 尺寸 51 layout.itemSize = CGSizeMake(180, 180); 52 // 设置滚动方向:水平 53 layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 54 // 设置额外滚动区域 55 CGFloat inset = (XMGScreenW - 180) * 0.5; 56 layout.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset); 57 58 layout; 59 }); 60 61 // 创建UICollectionView:默认为黑色 62 UICollectionView *collectionView = ({ 63 collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; 64 collectionView.bounds = CGRectMake(0, 0, self.view.bounds.size.width, 200); 65 collectionView.center = self.view.center; 66 collectionView.backgroundColor = [UIColor cyanColor]; 67 collectionView.showsHorizontalScrollIndicator = NO; 68 [self.view addSubview:collectionView]; 69 70 // 设置数据源 71 collectionView.dataSource = self; 72 collectionView; 73 }); 74 75 // 注册cell 76 [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([PhotoCell class]) bundle:nil] forCellWithReuseIdentifier:ID]; 77 } 78 79 80 #pragma mark -UICollectionViewDataSource 81 // 返回有多少个cell 82 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 83 { 84 return 20; 85 } 86 87 // 返回每个cell长什么样 88 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 89 { 90 91 PhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath]; 92 93 NSString *imageName = [NSString stringWithFormat:@"%ld",indexPath.row + 1]; 94 95 cell.image = [UIImage imageNamed:imageName]; 96 97 return cell; 98 } 99 100 @end