美文网首页iOS大咖iOS Developer
UICollectionViewController基础(3)瀑

UICollectionViewController基础(3)瀑

作者: 面皮大师 | 来源:发表于2017-06-08 14:55 被阅读76次

    瀑布流现在是一些APP常用的场景,如电子商城、旅游、社区等。实现起来很简单。
    项目github下载地址

    WaterfallCollectionViewCell

    先定义一个带XIB的WaterfallCollectionViewCell

    #import <UIKit/UIKit.h>
    
    @interface WaterfallCollectionViewCell : UICollectionViewCell
    
    @property (nonatomic,weak)IBOutlet UIImageView *image;
    
    @end
    
    

    XIB部分推荐运用AutoLayout进行布局,可以节省好多时间。

    XIB

    WaterfallCollectionViewLayout

    定义WaterfallCollectionViewLayout集成自UICollectionViewLayout,这里的思路是:

    • 实现瀑布流
    • 需要根据要现实的内容计算每个Item的高度?
    • 每个Item的上下左右边距是多少?
    • 瀑布流要现实多少列?
      那么问题来了要怎么去实现呢?这里需要运用到代理,一直有一些初学者对代理这个东西用起来感觉有点绕,其实可以这么理解。
    • 我是一个CollectionViewController
    • 我想要计算每个Item的宽高?
    • 我想要计算下一个Item要进行布局时,放在哪个列的下面?
    • 但这个事情我做不合适,为什么呢?
    • 因为我是一个大领导有好多事情要管,你们每个Item自己管好了,我给你们提供一个沟通渠道(代理实现的方法)告诉你们我想要什么就行,那么剩下的事情就委托给你们自己了。
    #import <UIKit/UIKit.h>
    
    @class WaterfallCollectionViewLayout;
    
    @protocol WaterfallCollectionViewDelegate <NSObject>
    
    @required
    - (CGFloat)waterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth;
    
    @optional
    - (NSInteger)columnCountInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout;
    - (CGFloat)columnMarginInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout;
    - (CGFloat)rowMarginInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout;
    - (UIEdgeInsets)edgeInsetsInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout;
    
    @end
    
    
    @interface WaterfallCollectionViewLayout : UICollectionViewLayout
    
    @property (nonatomic, weak) id<WaterfallCollectionViewDelegate> delegate;
    
    @end
    
    
    //
    //  WaterfallCollectionViewLayout.m
    //  UICollectionVIewDemo
    //
    //  Created by DaLei on 2017/6/8.
    //  Copyright © 2017年 DaLei. All rights reserved.
    //
    
    #import "WaterfallCollectionViewLayout.h"
    
    /** 默认的列数 */
    static const NSInteger DefaultColumnCount = 3;
    /** 每一列之间的间距 */
    static const CGFloat DefaultColumnMargin = 10;
    /** 每一行之间的间距 */
    static const CGFloat DefaultRowMargin = 10;
    /** 边缘间距 */
    static const UIEdgeInsets DefaultEdgeInsets = {10, 10, 10, 10};
    
    
    
    
    @interface WaterfallCollectionViewLayout()
    
    /** 存放所有cell的布局属性 */
    @property (nonatomic, strong) NSMutableArray *attrsArray;
    /** 存放所有列的当前高度 */
    @property (nonatomic, strong) NSMutableArray *columnHeights;
    /** 内容的高度 */
    @property (nonatomic, assign) CGFloat contentHeight;
    
    - (CGFloat)rowMargin;
    - (CGFloat)columnMargin;
    - (NSInteger)columnCount;
    - (UIEdgeInsets)edgeInsets;
    
    @end
    
    
    
    @implementation WaterfallCollectionViewLayout
    
    #pragma mark - 常见数据处理
    
    - (CGFloat)rowMargin {
        if ([self.delegate respondsToSelector:@selector(rowMarginInWaterflowLayout:)]) {
            return [self.delegate rowMarginInWaterflowLayout:self];
        } else {
            return DefaultRowMargin;
        }
    }
    
    - (CGFloat)columnMargin {
        if ([self.delegate respondsToSelector:@selector(columnMarginInWaterflowLayout:)]) {
            return [self.delegate columnMarginInWaterflowLayout:self];
        } else {
            return DefaultColumnMargin;
        }
    }
    
    - (NSInteger)columnCount {
        if ([self.delegate respondsToSelector:@selector(columnCountInWaterflowLayout:)]) {
            return [self.delegate columnCountInWaterflowLayout:self];
        } else {
            return DefaultColumnCount;
        }
    }
    
    - (UIEdgeInsets)edgeInsets {
        if ([self.delegate respondsToSelector:@selector(edgeInsetsInWaterflowLayout:)]) {
            return [self.delegate edgeInsetsInWaterflowLayout:self];
        } else {
            return DefaultEdgeInsets;
        }
    }
    
    #pragma mark - 懒加载
    
    - (NSMutableArray *)columnHeights {
        if (!_columnHeights) {
            _columnHeights = [NSMutableArray array];
        }
        return _columnHeights;
    }
    
    - (NSMutableArray *)attrsArray {
        if (!_attrsArray) {
            _attrsArray = [NSMutableArray array];
        }
        return _attrsArray;
    }
    
    - (void)prepareLayout {
        [super prepareLayout];
        
        self.contentHeight = 0;
        
        // 清除以前计算的所有高度
        [self.columnHeights removeAllObjects];
        for (NSInteger i = 0; i < self.columnCount; i++) {
            [self.columnHeights addObject:@(self.edgeInsets.top)];
        }
        
        // 清除之前所有的布局属性
        [self.attrsArray removeAllObjects];
        // 开始创建每一个cell对应的布局属性
        NSInteger count = [self.collectionView numberOfItemsInSection:0];
        for (NSInteger i = 0; i < count; i++) {
            // 创建位置
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
            // 获取indexPath位置cell对应的布局属性
            UICollectionViewLayoutAttributes *attrs = [self layoutAttributesForItemAtIndexPath:indexPath];
            [self.attrsArray addObject:attrs];
        }
    }
    
    /**
     * 决定cell的排布
     */
    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
        return self.attrsArray;
    }
    
    /**
     * 返回indexPath位置cell对应的布局属性
     */
    - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
        // 创建布局属性
        UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        
        // collectionView的宽度
        CGFloat collectionViewW = self.collectionView.frame.size.width;
        
        // 设置布局属性的frame
        CGFloat w = (collectionViewW - self.edgeInsets.left - self.edgeInsets.right - (self.columnCount - 1) * self.columnMargin) / self.columnCount;
        CGFloat h = [self.delegate waterflowLayout:self heightForItemAtIndex:indexPath.item itemWidth:w];
        
        // 找出高度最短的那一列
        NSInteger destColumn = 0;
        CGFloat minColumnHeight = [self.columnHeights[0] doubleValue];
        for (NSInteger i = 1; i < self.columnCount; i++) {
            // 取得第i列的高度
            CGFloat columnHeight = [self.columnHeights[i] doubleValue];
            
            if (minColumnHeight > columnHeight) {
                minColumnHeight = columnHeight;
                destColumn = i;
            }
        }
        
        CGFloat x = self.edgeInsets.left + destColumn * (w + self.columnMargin);
        CGFloat y = minColumnHeight;
        if (y != self.edgeInsets.top) {
            y += self.rowMargin;
        }
        attrs.frame = CGRectMake(x, y, w, h);
        
        // 更新最短那列的高度
        self.columnHeights[destColumn] = @(CGRectGetMaxY(attrs.frame));
        
        // 记录内容的高度
        CGFloat columnHeight = [self.columnHeights[destColumn] doubleValue];
        if (self.contentHeight < columnHeight) {
            self.contentHeight = columnHeight;
        }
        return attrs;
    }
    
    - (CGSize)collectionViewContentSize {
        return CGSizeMake(0, self.contentHeight + self.edgeInsets.bottom);
    }
    
    
    @end
    
    

    WaterfallCollectionViewController

    新建WaterfallCollectionViewController,像普通的CollectionViewController一样进行定义即可,并且实现WaterfallCollectionViewDelegate告诉Item需要现实几列,每隔Item有多高,每个Item的边距是多少。

    //
    //  WaterfallCollectionViewController.m
    //  UICollectionVIewDemo
    //
    //  Created by DaLei on 2017/6/8.
    //  Copyright © 2017年 DaLei. All rights reserved.
    //
    
    #import "WaterfallCollectionViewController.h"
    #import "WaterfallCollectionViewCell.h"
    #import "WaterfallCollectionViewLayout.h"
    
    #define RGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
    #define RandomRGBColor RGBColor(arc4random_uniform(255), arc4random_uniform(255), arc4random_uniform(255))
    
    @interface WaterfallCollectionViewController ()<WaterfallCollectionViewDelegate>
    
    @property(nonatomic,strong)NSArray *productArray;
    
    @end
    
    @implementation WaterfallCollectionViewController
    
    static NSString * const reuseIdentifier = @"WaterfallCollectionViewCell";
    
    -(instancetype)init{
        WaterfallCollectionViewLayout *layout = [WaterfallCollectionViewLayout new];
        layout.delegate = self;
        return [super initWithCollectionViewLayout:layout];
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.title = @"面皮大师-瀑布流";
        
        // Register cell classes
        [self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([WaterfallCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:reuseIdentifier];
        self.collectionView.backgroundColor = RGBColor(235, 235, 235);
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
    }
    
    -(NSArray *)productArray{
        if (!_productArray) {
            _productArray = @[@"cell1",@"cell2",@"cell3",@"cell4",@"cell5",@"cell6",@"cell7",@"cell1",@"cell2",@"cell3",@"cell4",@"cell5",@"cell6",@"cell7",@"cell1",@"cell2",@"cell3",@"cell4",@"cell5",@"cell6",@"cell7"];
        }
        return _productArray;
    }
    
    #pragma mark <UICollectionViewDataSource>
    
    
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
        return [self.productArray count];
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        WaterfallCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
        cell.backgroundColor = RandomRGBColor;
        UIImage *image = [UIImage imageNamed:[self.productArray objectAtIndex:indexPath.item]];
        [cell.image setImage:image];
        return cell;
    }
    
    #pragma mark <WaterfallCollectionViewDelegate>
    
    - (CGFloat)waterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout heightForItemAtIndex:(NSUInteger)index itemWidth:(CGFloat)itemWidth{
        // 获取图片的宽高,根据图片的比例计算Item的高度。
        UIImage *image = [UIImage imageNamed:[self.productArray objectAtIndex:index]];
        CGFloat fixelW = CGImageGetWidth(image.CGImage);
        CGFloat fixelH = CGImageGetHeight(image.CGImage);
        CGFloat itemHeight = fixelH * itemWidth / fixelW;
        return itemHeight + 50;
    }
    
    - (NSInteger)columnCountInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout{
        return 2;
    }
    
    - (CGFloat)columnMarginInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout{
        return 10;
    }
    
    - (CGFloat)rowMarginInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout{
        return 10;
    }
    
    - (UIEdgeInsets)edgeInsetsInWaterflowLayout:(WaterfallCollectionViewLayout *)waterflowLayout{
        return UIEdgeInsetsMake(10, 10, 10, 10);
    }
    
    @end
    
    

    运行效果:

    瀑布流

    学习参考:Developer_CYX

    UICollectionViewController基础(1)
    UICollectionViewController基础(2)引导页
    UICollectionViewController基础(3)瀑布流
    UICollectionViewController基础(4)自定义

    相关文章

      网友评论

      • Zszen:因为layout和controller就缠在一起 搞得很迷

      本文标题:UICollectionViewController基础(3)瀑

      本文链接:https://www.haomeiwen.com/subject/yjbafxtx.html