UICollectionView布局详解

来源:互联网 发布:小野丽莎 知乎 编辑:程序博客网 时间:2024/04/29 07:52

1、UICollectionViewController作为一个功能强大的UI控制器,在当下的开发中占据了很大的地位,在很多的应用中都可以找到他的身影。那么接下来我们就来详细演练一下他的几种用法。

一、实现线性布局的相册效果
这里写图片描述

1、首先分析可以知道 ,一般情况这种的线性布局我们布局直接去继承 UICollectionViewLayout的流水式布局UICollectionViewFlowLayout 就可以快速的实现,

注意点:
只有 UICollectionViewFlowLayout 可以去设置 图片的 滚动方向
//设置滚动方向
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
UICollectionViewLayout是没有这个属性的。

核心代码:

//准备布局- (void)prepareLayout{    // 必须 调用父类    [super prepareLayout];    //设置滚动方向    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;    //设置cell 的大小    CGFloat itemWH = self.collectionView.frame.size.height  * 0.7;    self.itemSize = CGSizeMake(itemWH, itemWH);    //设置内边距    CGFloat inset = (self.collectionView.frame.size.width - self.itemSize.width) * 0.5;    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);}/** * 返回CollectionView 上面 所有元素的布局属性 */- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {    // 调用父类方法 拿到默认的布局 属性    NSArray *array = [super layoutAttributesForElementsInRect:rect];    //获得CollectionView中点 的x值    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.frame.size.width * 0.5;    //在默认的布局属相的基础上进行 调整    for (UICollectionViewLayoutAttributes *attrs in array) {        // 计算 cell 中点 x 到 CollectionvIEW Z的值        CGFloat delta = ABS(attrs.center.x - centerX);        // 根据距离计算缩放比例  成反比        CGFloat scale = 1 - delta / (self.collectionView.frame.size.width + self.itemSize.width);        attrs.transform = CGAffineTransformMakeScale(scale, scale);    }    return array;}// 当 CollectionView 的bounds 发生变化 的时候刷新- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{    return YES;}/** * 获得最终的偏移量 */- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{    //1、计算 最终 的可见范围    CGRect rect;    rect.origin = proposedContentOffset;    rect.size = self.collectionView.frame.size;    //取得cell 的 布局属性    NSArray *array = [super  layoutAttributesForElementsInRect:rect];    // 计算CollectionView 中线的X值    CGFloat centerX = proposedContentOffset.x + self.collectionView.frame.size.width * 0.5;    CGFloat minDelta = MAXFLOAT;    for (UICollectionViewLayoutAttributes *attrs in array) {        CGFloat delta = attrs.center.x - centerX;        if (ABS(delta <= ABS(minDelta))) {            minDelta = delta;        }    }    return  CGPointMake(proposedContentOffset.x + minDelta, proposedContentOffset.y);}

二、实现 图片环形效果
这里写图片描述

我们的布局就要继承UICollectionViewLayout ,这个最纯洁的布局 ,一切都需要我们自己去实现。

注意两个我们布局 都需要的方法:
/**
* 决定cell 如何排布
*/
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

//返回 indexPath 对应cell 的 布局属性

  • (UICollectionViewLayoutAttributes ) layoutAttributesForItemAtIndexPath:(NSIndexPath )indexPath

一个是 所有cell 的属性 ,一个是 这个cell 具体的显示 ,我们 只要 把握好这两个方法就好了,看下列实例:

@implementation TSZCircleLayout/** * 决定cell 如何排布 */- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{    NSMutableArray *array = [NSMutableArray array];    NSUInteger count = [self.collectionView  numberOfItemsInSection:0];    for (int i = 0; i < count; i++) {        //创建i 位置cell 对应的indexPath        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];        // 创建i 位置cell 对应 的 布局属性        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];        //添加布局属性        [array addObject:attrs];    }    return  array;}//返回 indexPath 对应cell 的 布局属性- (UICollectionViewLayoutAttributes *) layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{    NSUInteger count = [self.collectionView numberOfItemsInSection:indexPath.section];    //半径    CGFloat radius = 100;    //圆心的坐标    CGFloat centenX = self.collectionView.frame.size.width * 0.5;    CGFloat centerY = self.collectionView.frame.size.height * 0.5;    //角度增量    CGFloat deltaAngle = 2 * M_PI / count;    //创建i 位置cell 对应的布局属性    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];    attrs.size = CGSizeMake(50, 50);    if (count ==  1) {        attrs.center = CGPointMake(centenX, centerY);    }else {        //旋转角度        CGFloat angle = indexPath.item * deltaAngle ;        //cell 中心点坐标        CGFloat attrsCenterX = centenX + radius * cos(angle);        CGFloat attrsCenterY = centerY + radius * sin(angle);        attrs.center = CGPointMake(attrsCenterX, attrsCenterY);    }    return  attrs;}@end

三、堆形布局
这里写图片描述

跟环形布局的思想一样 ,其实主要就是 你 想做出什么样的形状只要 你自己 想好cell 的 排列 就可以了

核心代码:

@implementation TSZStackLayout//实现布局就要实现两个方法#pragma mark 返回 所有cell的布局属性- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{    //1、定义存储属性的数组    NSMutableArray *array = [NSMutableArray array];    //2、获得需要布局的组对应的 个数    NSUInteger count = [self.collectionView numberOfItemsInSection:0];    for ( int i = 0; i < count;  i++) {        NSIndexPath *indexPath  =[NSIndexPath indexPathForItem:i inSection:0];        UICollectionViewLayoutAttributes *attrs =  [self layoutAttributesForItemAtIndexPath:indexPath  ];        [array addObject:attrs];    }    return  array;}#pragma  mark 每一个indexPath对应cell  的布局- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];    //获得 组中的item的个数    NSUInteger count = [self.collectionView numberOfItemsInSection:indexPath.section];    attrs.center = CGPointMake(self.collectionView.frame.size.width * 0.5 , self.collectionView.frame.size.height * 0.5);    attrs.size = CGSizeMake(100, 100);    //为了美观显示 5个的布局    if (count >= 5) {        count = 5;    }    if (indexPath.item >= count) {        attrs.hidden = YES;        return attrs;    }    CGFloat deltaAngle = M_PI_2 / count;    CGFloat angle = indexPath.item * deltaAngle;    attrs.transform = CGAffineTransformMakeRotation(angle);    return attrs;}

上面三种布局的实现可以参考我的gitHub地址下载 源代码研究:https://github.com/tangShunZhi/UICollectionViewLayouts

四、UICollectionView 实现 瀑布流效果
这里写图片描述
所谓的瀑布流的效果就是 使用 UICollectionLayout的布局去实现 参差不齐的的效果,不会出现视觉疲劳。原理就是在布局的方法中实现 我前面提过的那两个方法,实现布局就可以了。

原理:每次找到各列中最小的 Y偏移量,把下一个出现的cell 放在最小的那一列。

核心计算代码:

/** * 准备layout 的布局 */- (void)prepareLayout{    [super prepareLayout];    //重置每一列的最大的坐标    [self.columnMaxYArray removeAllObjects];    for (int i = 0  ; i < TSZDefaultColumnCount; i++) {        [self.columnMaxYArray addObject:@(self.edgeInsets.top)];    }    //计算所有cell 的 布局属性    [self.attrsArray removeAllObjects];    NSUInteger count = [self.collectionView  numberOfItemsInSection:0];    for (int i = 0; i< count; i++) {        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];        UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];        [self.attrsArray addObject:attrs];    }}// 决定collectionView 的ContentSize- (CGSize)collectionViewContentSize{    //找出最长的一列    CGFloat destColumnMaxY = [self.columnMaxYArray[0] doubleValue];    for (int i = 1; i < self.columnMaxYArray.count; i++) {        CGFloat columnMaxY = [self.columnMaxYArray[i] doubleValue];        if (columnMaxY > destColumnMaxY) {            destColumnMaxY = columnMaxY;        }    }    return  CGSizeMake(TSZCollectionViewWidth,destColumnMaxY + self.edgeInsets.bottom);}//数组中是所有元素最终显示出来的 布局属性- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{    NSMutableArray *array = [NSMutableArray array];    for (int i = 0 ; i < self.attrsArray.count; i++) {        UICollectionViewLayoutAttributes *attrs = self.attrsArray[i];        if (CGRectIntersectsRect(rect, attrs.frame)) {            [array addObject:attrs];        }    }    return array;}//说明indexPath 位置cell 的布局- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{    //每一个cell 的 布局    UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];    // 计算indexPath位置cell 的布局属性 , 找出最短的列号 和最大的y坐标    CGFloat  destColumnMaxY = [self.columnMaxYArray[0] doubleValue];    NSUInteger destColumnIndex = 0;    for ( int i = 1;  i < self.columnMaxYArray.count; i++) {        CGFloat columnMaxY = [self.columnMaxYArray[i] doubleValue];        if (columnMaxY < destColumnMaxY) {            destColumnMaxY = columnMaxY;            destColumnIndex = i;        }    }    CGFloat totalColumnSpacing = (self.columnCount -1) * self.columnSpacing;    CGFloat width = (TSZCollectionViewWidth - self.edgeInsets.left - self.edgeInsets.right - totalColumnSpacing) / TSZDefaultColumnCount;    CGFloat height = [self.delegate waterfallFlowLayout:self heightForItemAtIndexPath:indexPath withItemWidth:width];    CGFloat x = self.edgeInsets.left + destColumnIndex * (width + self.columnSpacing);    CGFloat y = destColumnMaxY;    if (destColumnMaxY != self.edgeInsets.top) {        y += self.rowSpacing;    }    attrs.frame  = CGRectMake(x, y, width, height);    //更新最大的坐标    self.columnMaxYArray[destColumnIndex] = @(CGRectGetMaxY(attrs.frame));    return attrs;}

完整的项目在 github:https://github.com/tangShunZhi/UICollectionViewFallLayout 下载源码 自己去 愉快的玩耍吧!!!

0 0
原创粉丝点击