iOS GZWaterfall任何形式的瀑布流

概述

使用UICollectionView能夠佈局各類各樣的瀑布流,下面我寫了幾種不一樣佈局的瀑布流樣式

詳細

首先,將全部的類型展現給你們;html

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

 

 

 

上面圖片中展現的樣式在Demo中都有實現。dom

 

1、項目結構

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

對於咱們要實現的各類各樣的 collectionView,根據不一樣的需求設置不一樣的列數 ,列邊距,行邊距,collectionView邊距async

2、程序實現

一、隨機瀑布流佈局

屏幕快照 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;
}

二、規則瀑布流spa

屏幕快照 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;
}

三、簡單兩排瀑布流3d

屏幕快照 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;
}

四、兩排瀑布流orm

屏幕快照 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);
    }

}

五、環形瀑布流htm

屏幕快照 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];
    }

六、立方瀑布流blog

屏幕快照 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;
}

七、球形瀑布流圖片

屏幕快照 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;

3、運行效果

屏幕快照 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大師發表,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索