Collectionview自定义布局

来源:互联网 发布:微信 java版 编辑:程序博客网 时间:2024/06/05 17:43

我们平时经常见到一些很酷的滚动效果,卡片式的堆叠效果、瀑布流、单元格移动等,这些炫酷的效果我们可以通过collectionview自定义布局来实现。

继承UICollectionViewLayout

我们自定义layout,需要继承UICollectionViewLayout,有一些核心方法需要我们来实现,这些核心方法可以帮助我们来完成布局任务。

  1. prepareLayout方法,为布局计算做一些准备工作;
  2. collectionViewContentSize方法,返回内容区域的size;
  3. layoutAttributesForElementsInRect:方法,返回矩形区域内cells或者views的属性;

说明图片:
这里写图片描述

示例代码

创建自定义布局类
collectionViewLayout.h 文件

interface collectionViewLayout : UICollectionViewLayoutproperty (nonatomic) CGSize itemSize;@property (nonatomic) NSInteger visibleCount;@property (nonatomic) UICollectionViewScrollDirection scrollDirection;@end

collectionViewLayout.m 文件

#import "collectionViewLayout.h"#define INTERSPACEPARAM  0.65@interface collectionViewLayout (){    CGFloat _viewHeight;    CGFloat _itemHeight;}@end
//为自定义布局做准备工作,确定滚动区域大小-(void)prepareLayout {    [super prepareLayout];    if (self.visibleCount < 1) {        self.visibleCount = 5;    }    _viewHeight = CGRectGetWidth(self.collectionView.frame);    _itemHeight = self.itemSize.width;    self.collectionView.contentInset = UIEdgeInsetsMake(0, (_viewHeight - _itemHeight) / 2, 0, (_viewHeight - _itemHeight) / 2);    self.collectionView.showsHorizontalScrollIndicator = NO;    self.collectionView.showsVerticalScrollIndicator = NO;}
//处理现实区域布局属性-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];    CGFloat centerY = self.collectionView.contentOffset.x + _viewHeight / 2;    NSInteger index = centerY / _itemHeight;    NSInteger count = (self.visibleCount - 1) / 2;    NSInteger minIndex = MAX(0, (index - count));    NSInteger maxIndex = MIN((cellCount - 1), (index + count));    NSMutableArray *array = [NSMutableArray array];    for (NSInteger i = minIndex; i <= maxIndex; i++) {        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];        [array addObject:attributes];    }    return array;}
//此方法可以立即获取某一布局属性- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];    attributes.size = self.itemSize;    CGFloat cY = self.collectionView.contentOffset.x + _viewHeight / 2;    CGFloat attributesY = _itemHeight * indexPath.row + _itemHeight / 2;    attributes.zIndex = -ABS(attributesY - cY);    CGFloat delta = cY - attributesY;    CGFloat ratio =  - delta / (_itemHeight * 2);    CGFloat scale = 1 - ABS(delta) / (_itemHeight * 6.0) * cos(ratio * M_PI_4);    attributes.transform = CGAffineTransformMakeScale(scale, scale);    CGFloat centerY = attributesY;    centerY = cY + sin(ratio * M_PI_2) * _itemHeight * INTERSPACEPARAM;    attributes.center = CGPointMake(centerY, CGRectGetHeight(self.collectionView.frame) / 2);    return attributes;}
//这个方法的返回值,就决定了collectionView停止滚动时的偏移量- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {    CGFloat index = roundf((proposedContentOffset.x + _viewHeight / 2 - _itemHeight / 2) / _itemHeight);    proposedContentOffset.x = _itemHeight * index + _itemHeight / 2 - _viewHeight / 2;    return proposedContentOffset;}
//这个方法的返回值,决定了当collectionview的显示范围发生改变的时候,是否需要刷新布局- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {    return !CGRectEqualToRect(newBounds, self.collectionView.bounds);}

使用自定义布局
ViewController.m

#import "CollectionCell.h"#import "collectionViewLayout.h"@interface ViewController ()<UICollectionViewDataSource,UICollectionViewDelegate>@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;@property (strong, nonatomic) NSArray * imageArray;@end
- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    _imageArray = @[@"p4.jpg",@"p0.jpg",@"p1.jpg",@"p2.jpg",@"p3.jpg",@"p4.jpg",@"p0.jpg",@"p1.jpg"];    collectionViewLayout * layout = [[collectionViewLayout alloc] init];    layout.itemSize = CGSizeMake(250, 160);    layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;    _collectionView.frame = CGRectMake(0, 100, self.view.frame.size.width, layout.itemSize.height);    _collectionView.backgroundColor = [UIColor grayColor];    [self.collectionView setCollectionViewLayout:layout animated:YES];    _collectionView.delegate = self;    _collectionView.dataSource = self;}-(void)viewDidAppear:(BOOL)animated{    [super viewDidAppear:animated];    //使collectionview默认滑动到第二张图片位置    _collectionView.contentOffset = CGPointMake(250, 0);}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {    return _imageArray.count;}- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {    CollectionCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath];    cell.imageView.image = [UIImage imageNamed:_imageArray[indexPath.row]];    return cell;}- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {    NSIndexPath *curIndexPath = [self curIndexPath];    if (indexPath.row == curIndexPath.row) {        return YES;    }    NSLog(@"current row %ld",(long)indexPath.row);    [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionNone animated:YES];    return NO;}//获取当前点击的IndexPath- (NSIndexPath *)curIndexPath {    NSArray *indexPaths = [self.collectionView indexPathsForVisibleItems];    NSIndexPath *curIndexPath = nil;    NSInteger curzIndex = 0;    for (NSIndexPath *path in indexPaths.objectEnumerator) {        UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:path];        if (!curIndexPath) {            curIndexPath = path;            curzIndex = attributes.zIndex;            continue;        }        if (attributes.zIndex > curzIndex) {            curIndexPath = path;            curzIndex = attributes.zIndex;        }    }    return curIndexPath;}
//实现无限循环滚动-(void)scrollViewDidScroll:(UIScrollView *)scrollView{    if (scrollView.contentOffset.x<=250) {        scrollView.contentOffset = CGPointMake(5*250+scrollView.contentOffset.x, 0);    }    if (scrollView.contentOffset.x > (_imageArray.count - 2)*250) {        scrollView.contentOffset = CGPointMake(250+scrollView.contentOffset.x-(_imageArray.count - 2)*250, 0);    }}

效果如下:
这里写图片描述

本示例还存在一个问题,就是点击非焦点cell时不能使其很好的滚动到中间位置,望大神们指点一下。

1 0
原创粉丝点击