自定义流水布局(UICollectionViewFlowLayout的基本使用)
最终显示的效果图
思路:
1、UICollection的基本设置,并且创建一个继承自UICollectionViewFlowLayout的类。(不能是UICollectionViewLayout,否则全部都需要自定义)
2、在UICollectionViewFlowLayout类中完成四步
- 1)重写prepareLayout方法进行基本的布局(cell在最左面的时候是在正中间),不能在init中布局,因为设置collectionView尺寸是在viewDidLoad中,而init在它之前调用,获得的collectionView的尺寸是空的
- 2)重写shouldInvalidateLayoutForBoundsChange,当collectionView的显示范围发生改变的时候,让其内部重新布局(即让cell滚动起来)
- 3)重写layoutAttributesForElementsInRect方法,让cell在左右滑动的时候,尺寸放大或缩小
- 4)重写targetContentOffsetForProposedContentOffset方法,让最接近中心的cell在停在正中央。
代码如下:
viewContorller中:
1 #import "ViewController.h" 2 #import "ZWLineLayout.h" 3 @interface ViewController () <UICollectionViewDataSource> 4 @end 5 @implementation ViewController 6 static NSString *ZWCellID = @"cell"; 7 - (void)viewDidLoad { 8 [super viewDidLoad]; 9 //若为UICollectionViewLayout,itemSize和scrollDirection都需要自己写,下面的类继承自UICollectionViewLayout 10 ZWLineLayout *layout = [[ZWLineLayout alloc] init]; 11 layout.itemSize = CGSizeMake(160, 160); 12 CGRect rect = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width * 0.6); 13 UICollectionView *collection = [[UICollectionView alloc] initWithFrame:rect collectionViewLayout:layout]; 14 collection.dataSource = self; 15 collection.backgroundColor = [UIColor greenColor]; 16 [self.view addSubview:collection]; 17 [collection registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:ZWCellID]; 18 } 19 20 #pragma mark - 数据源方法 21 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section 22 { 23 return 10; 24 } 25 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 26 { 27 UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ZWCellID forIndexPath:indexPath]; 28 cell.backgroundColor = [UIColor orangeColor]; 29 return cell; 30 }
ZWLineLayout.m中
1 #import "ZWLineLayout.h" 2 3 @implementation ZWLineLayout 4 5 /** 6 * 用来做布局的初始化操作(不建议在init方法中进行布局的初始化操作) 7 */ 8 - (void)prepareLayout 9 { 10 [super prepareLayout]; 11 //水平滚动 12 self.scrollDirection = UICollectionViewScrollDirectionHorizontal; 13 14 // 15 CGFloat margin = (self.collectionView.frame.size.width - self.itemSize.width) / 2; 16 self.collectionView.contentInset = UIEdgeInsetsMake(0, margin, 0, margin); 17 } 18 19 /** 20 * 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局 21 * 一旦重新刷新布局,就会重新调用下面的方法: 22 * 1.prepareLayout 23 * 2.layoutAttributesForElementsInRect:方法 24 */ 25 - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 26 { 27 return YES; 28 } 29 30 31 /** 32 * 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性) 33 * 这个方法的返回值决定了rect范围内所有元素的排布(frame) 34 */ 35 //需要在viewController中使用上ZWLineLayout这个类后才能重写这个方法!! 36 - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect 37 { 38 //让父类布局好样式 39 NSArray *arr = [super layoutAttributesForElementsInRect:rect]; 40 //计算出collectionView的中心的位置 41 CGFloat ceterX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5; 42 /** 43 * 1.一个cell对应一个UICollectionViewLayoutAttributes对象 44 * 2.UICollectionViewLayoutAttributes对象决定了cell的frame 45 */ 46 for (UICollectionViewLayoutAttributes *attributes in arr) { 47 //cell的中心点距离collectionView的中心点的距离,注意ABS()表示绝对值 48 CGFloat delta = ABS(attributes.center.x - ceterX); 49 //设置缩放比例 50 CGFloat scale = 1.1 - delta / self.collectionView.frame.size.width; 51 //设置cell滚动时候缩放的比例 52 attributes.transform = CGAffineTransformMakeScale(scale, scale); 53 } 54 55 return arr; 56 } 57 58 /** 59 * 这个方法的返回值,就决定了collectionView停止滚动时的偏移量 60 */ 61 - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity 62 { 63 // 计算出最终显示的矩形框 64 CGRect rect; 65 rect.origin.y = 0; 66 rect.origin.x = proposedContentOffset.x; 67 rect.size = self.collectionView.frame.size; 68 69 //获得super已经计算好的布局的属性 70 NSArray *arr = [super layoutAttributesForElementsInRect:rect]; 71 72 //计算collectionView最中心点的x值 73 CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5; 74 75 CGFloat minDelta = MAXFLOAT; 76 for (UICollectionViewLayoutAttributes *attrs in arr) { 77 if (ABS(minDelta) > ABS(attrs.center.x - centerX)) { 78 minDelta = attrs.center.x - centerX; 79 } 80 } 81 proposedContentOffset.x += minDelta; 82 return proposedContentOffset; 83 } 84 @end