zoukankan      html  css  js  c++  java
  • iOS GZWaterfall任何形式的瀑布流

    概述

    使用UICollectionView可以布局各种各样的瀑布流,下面我写了几种不同布局的瀑布流样式

    详细

    首先,将所有的类型展示给大家;

    屏幕快照 2017-08-09 14.42.22.png

    上面图片中展示的样式在Demo中都有实现。

    一、项目结构

    屏幕快照 2017-08-08 10.15.13.png

    对于我们要实现的各种各样的 collectionView,根据不同的需求设置不同的列数 ,列边距,行边距,collectionView边距

    二、程序实现

    1、随机瀑布流

    屏幕快照 2017-08-09 15.48.30.png

    #pragma mark - 创建collectionView
    - (void)setupCollectionView
    {
        GZFallsLayout *fallsLayout = [[GZFallsLayout alloc] init];
        fallsLayout.delegate = self;
        UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:fallsLayout];
        [self.view addSubview:collectionView];
        _collectionView = collectionView;
        collectionView.dataSource = self;
        [collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([GZShopCell class]) bundle:nil] forCellWithReuseIdentifier:ID];
    }
    
    #pragma mark - 创建上下拉刷新
    - (void)setupRefresh
    {
        self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewShops)];
        self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreShops)];
        self.collectionView.backgroundColor = [UIColor whiteColor];
        [self.collectionView.mj_header beginRefreshing];
    }
    
    #pragma mark - 加载下拉数据
    - (void)loadNewShops
    {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSArray *shops = [GZShop mj_objectArrayWithFilename:@"1.plist"];
            [weakSelf.shops removeAllObjects];
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.collectionView reloadData];
                [weakSelf.shops addObjectsFromArray:shops];
                [weakSelf.collectionView.mj_header endRefreshing];
                [weakSelf.collectionView reloadData];
            });
        });
    }
    
    #pragma mark - 加载上拉数据
    - (void)loadMoreShops
    {
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSArray *shops = [GZShop mj_objectArrayWithFilename:@"1.plist"];
            [weakSelf.shops addObjectsFromArray:shops];
            dispatch_async(dispatch_get_main_queue(), ^{
                [weakSelf.collectionView.mj_footer endRefreshing];
                [weakSelf.collectionView reloadData];
            });
        });
    }
    
    - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
    {
        return 1;
    }
    
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        self.collectionView.mj_footer.hidden = self.shops.count == 0;
        return self.shops.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        GZShopCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ID forIndexPath:indexPath];
        if (self.shops && self.shops.count >= indexPath.item+1) cell.shop = self.shops[indexPath.item];
        return cell;
    }
    
    - (CGFloat)columnMarginInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 5;
    }
    
    - (CGFloat)rowMarginInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 5;
    }
    
    - (CGFloat)columnCountInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return 4;
    }
    
    - (UIEdgeInsets)edgeInsetsInFallsLayout:(GZFallsLayout *)fallsLayout
    {
        return UIEdgeInsetsMake(0, 10, 20, 10);
    }
    
    - (NSMutableArray *)shops
    {
        if (!_shops) {
            _shops = [NSMutableArray array];
        }
        return _shops;
    }
    
    // 计算布局属性
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        // 每个collectionView的宽度
        CGFloat collectionViewW = self.collectionView.frame.size.width;
        // 每个cell的宽度
        CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right -
                     self.columnMargin * (self.columnCount - 1)) / self.columnCount;
        // cell的高度
        NSUInteger randomOfHeight = arc4random() % 100;
        CGFloat h = w * (randomOfHeight >= 50 ? 250 : 320) / 200;
        
        // cell应该拼接的列数
        NSInteger destColumn = 0;
        
        // 高度最小的列数高度
        CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
        // 获取高度最小的列数
        for (NSInteger i = 1; i < self.columnCount; i++) {
            CGFloat columnHeight = [self.columnHeights[i] doubleValue];
            if (minColumnHeight > columnHeight) {
                minColumnHeight = columnHeight;
                destColumn = i;
            }
        }
        
        // 计算cell的x
        CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
        // 计算cell的y
        CGFloat y = minColumnHeight;
        if (y != self.edgeInsets.top) {
            y += self.rowMargin;
        }
        
        // 随机数,用来随机生成大尺寸cell
        NSUInteger randomOfWhetherDouble = arc4random() % 100;
        // 判断是否放大
        if (destColumn < self.columnCount - 1                               // 放大的列数不能是最后一列(最后一列方法超出屏幕)
            && _noneDoubleTime >= 1                                         // 如果前个cell有放大就不放大,防止连续出现两个放大
            && (randomOfWhetherDouble >= 45 || _noneDoubleTime >= 8)        // 45%几率可能放大,如果累计8次没有放大,那么满足放大条件就放大
            && [self.columnHeights[destColumn] doubleValue] == [self.columnHeights[destColumn + 1] doubleValue] // 当前列的顶部和下一列的顶部要对齐
            && _lastDoubleIndex != destColumn) {             // 最后一次放大的列不等当前列,防止出现连续两列出现放大不美观
            _noneDoubleTime = 0;
            _lastDoubleIndex = destColumn;
            // 重定义当前cell的布局:宽度*2,高度*2
            attrs.frame = CGRectMake(x, y, w * 2 + self.columnMargin, h * 2 + self.rowMargin);
            // 当前cell列的高度就是当前cell的最大Y值
            self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
            // 当前cell列下一列的高度也是当前cell的最大Y值,因为cell宽度*2,占两列
            self.columnHeights[destColumn + 1] = @(CGRectGetMaxY(attrs.frame));
        } else {
            // 正常cell的布局
            if (_noneDoubleTime <= 3 || _lastFixIndex == destColumn) {                     // 如果没有放大次数小于3且当前列等于上次矫正的列,就不矫正
                attrs.frame = CGRectMake(x, y, w, h);
            } else if (self.columnHeights.count > destColumn + 1                         // 越界判断
                && y + h - [self.columnHeights[destColumn + 1] doubleValue] < w * 0.1) { // 当前cell填充后和上一列的高度偏差不超过cell最大高度的10%,就和下一列对齐
                attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn + 1] doubleValue] - y);
                _lastFixIndex = destColumn;
            } else if (destColumn >= 1                                                   // 越界判断
                       && y + h - [self.columnHeights[destColumn - 1] doubleValue] < w * 0.1) { // 当前cell填充后和上上列的高度偏差不超过cell最大高度的10%,就和下一列对齐
                attrs.frame = CGRectMake(x, y, w, [self.columnHeights[destColumn - 1] doubleValue] - y);
                _lastFixIndex = destColumn;
            } else {
                attrs.frame = CGRectMake(x, y, w, h);
            }
            // 当前cell列的高度就是当前cell的最大Y值
            self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
            _noneDoubleTime += 1;
        }
        // 返回计算获取的布局
        return attrs;
    }

    2、规则瀑布流

    屏幕快照 2017-08-09 15.56.19.png

    // 计算布局属性
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        if (indexPath.item == 0) {
            attrs.frame = CGRectMake(0, 0, ([UIScreen mainScreen].bounds.size.width - 4)/3*2 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3*2 +2);
        }else if (indexPath.item == 1){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, 0, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 2){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 3){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3*2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 + 4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else if (indexPath.item == 4){
            attrs.frame = CGRectMake(([UIScreen mainScreen].bounds.size.width - 4)/3 +2, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }else{
            attrs.frame = CGRectMake(0, ([UIScreen mainScreen].bounds.size.width - 4)/3 *2 +4, ([UIScreen mainScreen].bounds.size.width - 4)/3, ([UIScreen mainScreen].bounds.size.width - 4)/3);
        }
        // 返回计算获取的布局
        return attrs;
    }

    3、简单两排瀑布流

    屏幕快照 2017-08-09 15.58.30.png

    -(CGSize)collectionViewContentSize
    {
        //计算整个contentsize的大小
        __block CGFloat height=0;
        [arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if([obj floatValue]>height)
            {
                height=[obj floatValue];
            }
        }];
        return CGSizeMake(self.collectionView.bounds.size.width, height);
    }
    
    -(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        //计算每一个item的相关属性
        UICollectionViewLayoutAttributes *attr=[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        int index=0;
        if([arr[0] floatValue]<[arr[1] floatValue])
        {
            index=0;
        }
        else
        {
            index=1;
        }
        CGFloat width=self.collectionView.bounds.size.width/2;
        CGFloat height=arc4random()%200+100;
        CGFloat left=index*width;
        CGFloat top=[arr[index] floatValue];
        CGRect frame=CGRectMake(left, top, width, height);
        attr.frame=frame;
        arr[index]=@([arr[index] floatValue]+height);
        return attr;
    }

    4、两排瀑布流

    屏幕快照 2017-08-09 15.59.10.png

    -(void)prepareLayout{
    
        _attributeArray = [NSMutableArray array];
        [super prepareLayout];
        float WIDTH = ([UIScreen mainScreen].bounds.size.width-self.sectionInset.left-self.sectionInset.right-self.minimumInteritemSpacing)/2;
        CGFloat colHight[2] = {self.sectionInset.top,self.sectionInset.bottom};
        for (int i=0; i<_itemCount; i++) {
            
            NSIndexPath * index = [NSIndexPath indexPathForItem:i inSection:0];
            UICollectionViewLayoutAttributes * attribute =  [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
            CGFloat height = arc4random()%150+40;
            int width = 0;
            if (colHight[0]<colHight[1]) {
                colHight[0] = colHight[0]+height+self.minimumLineSpacing;
                width = 0; 
            }else{
                colHight[1] = colHight[1]+height+self.minimumLineSpacing;
                width = 1;
            }
            attribute.frame = CGRectMake(self.sectionInset.left+(self.minimumInteritemSpacing+WIDTH)*width, colHight[width]-height-self.minimumLineSpacing, WIDTH, height);
            [_attributeArray addObject:attribute];
            
        }
        if (colHight[0]>colHight[1]) {
            self.itemSize = CGSizeMake(WIDTH, (colHight[0]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);
        }else{
            self.itemSize = CGSizeMake(WIDTH, (colHight[1]-self.sectionInset.top)*2/_itemCount-self.minimumLineSpacing);
        }
    
    }

    5、环形瀑布流

    屏幕快照 2017-08-09 16.00.22.png

     _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
        _attributeArray = [NSMutableArray array];
        CGFloat radius  =MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/2;
        CGPoint center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2);
        for (int i=0; i<_itemCount; i++) {
            UICollectionViewLayoutAttributes * attris = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
            attris.size = CGSizeMake(50, 50);
            float x = center.x+cosf(2*M_PI/_itemCount*i)*(radius-25);
            float y = center.y+sinf(2*M_PI/_itemCount*i)*(radius-25);
            attris.center = CGPointMake(x, y);
            [_attributeArray addObject:attris];
        }

    6、立方瀑布流

    屏幕快照 2017-08-09 16.02.21.png

    -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
        //创建一个item布局属性类
        UICollectionViewLayoutAttributes * atti = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        //获取item的个数
        int itemCounts = (int)[self.collectionView numberOfItemsInSection:0];
        //设置每个item的大小为260*100
        atti.size = CGSizeMake(260, 100);
        atti.center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);
        CATransform3D tran3d = CATransform3DIdentity;
        tran3d.m34 = -1/2000.0;
        CGFloat radius = 50/tanf(M_PI*2/itemCounts/2);
    //     CGFloat angle = (float)(indexPath.row)/itemCounts*M_PI*2;
        //获取当前的偏移量
        float offset = self.collectionView.contentOffset.y;
        //在角度设置上,添加一个偏移角度
        float angleOffset = offset/self.collectionView.frame.size.height;
        CGFloat angle = (float)(indexPath.row+angleOffset-1)/itemCounts*M_PI*2;
        tran3d = CATransform3DRotate(tran3d, angle, 1.0, 0, 0);
        tran3d = CATransform3DTranslate(tran3d, 0, 0, radius);
        //进行设置
        atti.transform3D = tran3d;
        return atti;
    }

    7、球形瀑布流

    屏幕快照 2017-08-09 16.05.13.png

     UICollectionViewLayoutAttributes * atti = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        //获取item的个数
        int itemCounts = (int)[self.collectionView numberOfItemsInSection:0];
        atti.center = CGPointMake(self.collectionView.frame.size.width/2+self.collectionView.contentOffset.x, self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);
        atti.size = CGSizeMake(30, 30);
        
        CATransform3D trans3D = CATransform3DIdentity;
        trans3D.m34 = -1/900.0;
        
        CGFloat radius = 15/tanf(M_PI*2/itemCounts/2);
        //根据偏移量 改变角度
        //添加了一个x的偏移量
        float offsety = self.collectionView.contentOffset.y;
        float offsetx = self.collectionView.contentOffset.x;
        //分别计算偏移的角度
        float angleOffsety = offsety/self.collectionView.frame.size.height;
        float angleOffsetx = offsetx/self.collectionView.frame.size.width;
        CGFloat angle1 = (float)(indexPath.row+angleOffsety-1)/itemCounts*M_PI*2;
        //x,y的默认方向相反
        CGFloat angle2 = (float)(indexPath.row+angleOffsetx-1)/itemCounts*M_PI*2;
        //这里我们进行四个方向的排列
        if (indexPath.row%4==1) {
            trans3D = CATransform3DRotate(trans3D, angle1, 1.0,0, 0);
        }else if(indexPath.row%4==2){
            trans3D = CATransform3DRotate(trans3D, angle2, 0, 1, 0);
        }else if(indexPath.row%4==3){
            trans3D = CATransform3DRotate(trans3D, angle1, 0.5,0.5, 0);
        }else{
            trans3D = CATransform3DRotate(trans3D, angle1, 0.5,-0.5,0);
        }
        
        trans3D = CATransform3DTranslate(trans3D, 0, 0, radius);
        
        atti.transform3D = trans3D;

    三、运行效果

    屏幕快照 2017-08-09 15.48.30.png屏幕快照 2017-08-09 15.56.19.png屏幕快照 2017-08-09 16.02.21.png屏幕快照 2017-08-09 16.05.13.png屏幕快照 2017-08-09 16.00.22.png屏幕快照 2017-08-09 15.59.10.png屏幕快照 2017-08-09 15.58.30.png

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    姐姐的vue(1)
    LeetCode 64. Minimum Path Sum 20170515
    LeetCode 56. 56. Merge Intervals 20170508
    LeetCode 26. Remove Duplicates from Sorted Array
    LeetCode 24. Swap Nodes in Pairs 20170424
    LeetCode 19. Remove Nth Node From End of List 20170417
    LeetCode No.9 Palindrome Number 20170410
    LeetCode No.8. String to Integer (atoi) 2017/4/10(补上一周)
    LeetCode No.7 Reverse Integer 2017/3/27
    LeetCode No.4 Median of Two Sorted Arrays 20170319
  • 原文地址:https://www.cnblogs.com/demodashi/p/8491087.html
Copyright © 2011-2022 走看看