iOS编程------集合视图之瀑布流WaterFlowLayout

//
//  AppDelegate.h
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import 

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;


@end







//
//  AppDelegate.m
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import "AppDelegate.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    return YES;
}

@end






///////////////////////////////////////////////////////////////////





//
//  ViewController.h
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import 

@interface ViewController : UIViewController


@end







//
//  ViewController.m
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import "ViewController.h"
#import "WaterFlowLayout.h"
#import "ImageCell.h"
#import "Model.h"
#import "UIImageView+WebCache.h" //sd分类.导入第三方类库.

@interface ViewController ()<WaterFlowLayoutDelegate, UICollectionViewDelegate, UICollectionViewDataSource>

@property (nonatomic, strong) NSMutableArray *dataArray; //存放model的数组

@end



@implementation ViewController

//本地解析方法
- (void)setData{

    //1.获取文件路径
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];

    //2.转换为data
    NSData *data = [NSData dataWithContentsOfFile:filePath];

    //3.json解析
    NSArray *rootArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    self.dataArray = [NSMutableArray array];

    for (NSDictionary *dic in rootArray) {

        Model *model = [[Model alloc] init];
        [model setValuesForKeysWithDictionary:dic];
        [_dataArray addObject:model];

    }

}


- (void)viewDidLoad {
    [super viewDidLoad];

    //进行本地解析
    [self setData];

    //1.创建waterFlowLayout
    WaterFlowLayout *waterFlowLayout = [[WaterFlowLayout alloc] init];

    //列数
    waterFlowLayout.numberOfColumn = 3;

    //间距
    waterFlowLayout.insertItemSpacing = 10;

    //边距
    waterFlowLayout.sectionInsets = UIEdgeInsetsMake(20, 20, 20, 20);

    //itemSize
    CGFloat width = ([UIScreen mainScreen].bounds.size.width - 2 * 20 - 10 * 2) / 3;

    waterFlowLayout.itemSize = CGSizeMake(width, width);

    //设置代理
    waterFlowLayout.delegate = self;



    //2.创建collectionView
    UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:waterFlowLayout];

    collectionView.delegate = self;
    collectionView.dataSource = self;

    //注册单元格
    [collectionView registerClass:[ImageCell class] forCellWithReuseIdentifier:@"cell"];

    [self.view addSubview:collectionView];


}

#pragma mark----------dataSource--------

//单元元素数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    return _dataArray.count;
}

//加载单元格
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    //1.取出
    ImageCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];

    //2.赋值
    //imageView赋值image

    //model
    Model *model = _dataArray[indexPath.item];
    //设置图片
    [cell.myImageView sd_setImageWithURL:[NSURL URLWithString:model.thumbURL]];

    //3.返回
    return cell;

}

#pragma mark----------瀑布流代理方法---------

- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath {

    //图片缩放前的宽高比和缩放后的 宽高比一样,则不失真

    /*
     原本宽度        缩放宽度
     ______   =     ______

     原本高度        缩放高度

     缩放宽度指定,获取缩放后的高度

                          原本高度
     缩放高度 = 缩放宽度 *   ______
                          原本宽度

     height = width * model.height / model.width


     */

    //model
    Model *model =  _dataArray[indexPath.item];

    CGFloat width = ([UIScreen mainScreen].bounds.size.width - 2 * 20 - 10 * 2) / 3;

    CGFloat height = width * model.height / model.width;

    return height;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}




@end








/////////////////////////////////////////////////////////////////////






//
//  WaterFlowLayout.h
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import 

@protocol WaterFlowLayoutDelegate <NSObject>

//协议方法,用来获取item的高度
- (CGFloat)heightForItemIndexPath:(NSIndexPath *)indexPath;

@end


@interface WaterFlowLayout : UICollectionViewLayout

//瀑布流布局

//瀑布流布局需要指定的布局因素 itemSize , 边距 sectionInsets , 间距 insertItemSpacing , 列数 numberOfColumn , 代理 delegate

//存储列长度的数组heightColumns , items 的属性(宽, 高, x, y)

@property (nonatomic, assign) CGSize itemSize;                      //itemSize
@property (nonatomic, assign) CGFloat insertItemSpacing;            //间距
@property (nonatomic, assign) UIEdgeInsets sectionInsets;           //边距
@property (nonatomic, assign) NSInteger numberOfColumn;             //列数
@property (nonatomic, assign) id delegate; //代理


@end







//
//  WaterFlowLayout.m
//  UI21_WaterFlowLayout
//
//  Created by l on 15/10/5.
//  Copyright (c) 2015年 . All rights reserved.
//

#import "WaterFlowLayout.h"

@interface WaterFlowLayout ()

//分区里面元素的个数
@property (nonatomic, assign) NSInteger numberOfItems;

//存储列高度的数组 columnHeights
@property (nonatomic, strong) NSMutableArray *columnHeights;

//存储item属性的数组,itemAttributes
@property (nonatomic, strong) NSMutableArray *itemAttributes;


//获取最长列的下标
- (NSInteger)indexForLongestColumn;

//获取最短列的下标
- (NSInteger)indexForShortestColumn;

@end




@implementation WaterFlowLayout

//懒加载 给数据容器开辟空间.
//何为懒加载? 没有写在init和viewDidLoad里面,而是写在了get方法里面,只有当用到该值的时候,才加载数据.
//优点:节省了内存开辟.

- (NSMutableArray *)columnHeights {

    if (!_columnHeights) {

        self.columnHeights = [NSMutableArray array];
    }
    return _columnHeights;
}

- (NSMutableArray *)itemAttributes {

    if (!_itemAttributes) {

        self.itemAttributes = [NSMutableArray array];
    }
    return _itemAttributes;
}


//求最长列的下标
- (NSInteger)indexForLongestColumn {

    //最长列下标
    NSInteger longestIndex = 0;

    //最长列高度
    NSInteger longestHeight = 0; //设置为0,确保可以跟每一列进行比较.

    //循环遍历,数组中每个元素,进行比较,找出最长列下标.
    for (int i = 0; i < _columnHeights.count; i++) {

        //当前i元素的高度
        CGFloat currentHeight = [_columnHeights[i] floatValue];

        //比较
        if (currentHeight > longestHeight) {

            longestHeight = currentHeight;
            longestIndex = i;
        }

    }

    return longestIndex;
}


//求最小列下标
- (NSInteger)indexForShortestColumn {

    //最小列下标
    NSInteger shortestIndex = 0;

    //最小列高度
    CGFloat shortestHeight = CGFLOAT_MAX; // 设置为浮点数最大值,确保可以进行比较

    //遍历,找出最小值下标
    for (int i = 0; i < _columnHeights.count; i++) {

        //当前列i的高度
        CGFloat currentHeight = [_columnHeights[i] floatValue];

        if (currentHeight < shortestHeight) {
            shortestHeight = currentHeight;
            shortestIndex = i;
        }
    }

    return shortestIndex;
}


//重写准备布局方法
- (void)prepareLayout {

    //必须调用 super prepareLayout
    [super prepareLayout];

    //1.给存储列高度的数组,附上top值
    for (int i = 0; i < _numberOfColumn; i++) {

        //类型转换, 把CGFloat转换为对象类型
        self.columnHeights[i] = @(self.sectionInsets.top);

    }

    //2.获取分区中元素的个数
    self.numberOfItems = [self.collectionView numberOfItemsInSection:0];

    //3.给每一个item设置 frame和indexPath
    for (int i = 0; i < _numberOfItems; i++) {

        //(1)当前item的indexPath
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

        //(2)获取item的layoutAttributes
        UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

        //(3)给layoutAttributes的frame赋值

        //最小列下标
        NSInteger shortestIndex = [self indexForShortestColumn];

        //最小列高度
        CGFloat shortestHeight = [_columnHeights[shortestIndex] floatValue];

        //元素的x值 = 左边距left + (item宽度 + 水平间距) * 最小列下标(0 , 1, 2, ......)
        //itemX
        CGFloat itemX = _sectionInsets.left + (_itemSize.width + _insertItemSpacing) * shortestIndex;

        //itemY
        CGFloat itemY = shortestHeight + _insertItemSpacing;

        //itemWidth
        CGFloat itemWidth = _itemSize.width;

        //itemHeight
        //itemHeight 由代理执行的协议方法来获取,协议方法有返回值,为item高度.
        CGFloat itemHeight = 0; //定义

        if (_delegate != nil && [_delegate respondsToSelector:@selector(heightForItemIndexPath:)]) {
            itemHeight = [_delegate heightForItemIndexPath:indexPath];
        }

        //给layoutAttributes的frame赋值.
        layoutAttributes.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);


        //4.把赋值好的layoutAttributes存放到数组里面.
        [self.itemAttributes addObject:layoutAttributes];

        //5.更新最短列的高度
        //高度 = itemY + itemHeight
        self.columnHeights[shortestIndex] = @(itemY + itemHeight);

    }

}


//重写collectionViewContentSize方法, 确定最终高度.
- (CGSize)collectionViewContentSize {

    //获取最长列下标
    NSInteger longestIndex = [self indexForLongestColumn];

    //获取高度
    CGFloat longestHeight = [self.columnHeights[longestIndex] floatValue];

    //获取当前的contentSize  frame.size
    CGSize contentSize = self.collectionView.frame.size;

    //更改contentSize的高度
    contentSize.height = longestHeight;

    //返回
    return contentSize;
}


//重写 返回每一个元素的布局属性
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    //返回存储每一个单元格frame的数组.
    return self.itemAttributes;
}



@end









////////////////////////////////////////////////////////////////////






//
//  ImageCell.h
//  UI21_瀑布流
//
//  Created by  on 15/10/4.
//  Copyright © 2015年 littledogboy. All rights reserved.
//

#import 

@interface ImageCell : UICollectionViewCell

@property (nonatomic,strong)UIImageView *myImageView;


@end








//
//  ImageCell.m
//  UI21_瀑布流
//
//  Created by  on 15/10/4.
//  Copyright © 2015年 littledogboy. All rights reserved.
//

#import "ImageCell.h"

@implementation ImageCell

// 重写initWithFrame 方法,添加内部控件
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.myImageView = [[UIImageView alloc] initWithFrame:self.bounds];
        [self.contentView addSubview:_myImageView];
    }
    return self;
}


// 重写布局子视图方法, 给
- (void)layoutSubviews
{

    [super layoutSubviews];

    self.myImageView.frame = self.bounds;
}

@end






////////////////////////////////////////////////////////////////////







//
//  Model.h
//  UI21_瀑布流
//
//  Created by  on 15/10/5.
//  Copyright © 2015年 littledogboy. All rights reserved.
//

#import 
#import 

@interface Model : NSObject

@property(nonatomic, copy) NSString *thumbURL;
@property(nonatomic, assign) CGFloat width;
@property(nonatomic, assign) CGFloat height;

@end







//
//  Model.m
//  UI21_瀑布流
//
//  Created by  on 15/10/5.
//  Copyright © 2015年 littledogboy. All rights reserved.
//

#import "Model.h"

@implementation Model

-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{

}


-(void)setValue:(id)value forKey:(NSString *)key
{
    [super setValue:value forKey:key];

    if ([key isEqualToString:@"width"]) {
        self.width = [value floatValue]; // 主意要转化为 floatValue 类型
    }

    if ([key isEqualToString:@"height"]) {
        self.height = [value floatValue];
    }
}


@end


你可能感兴趣的:(界面,ui,ios,编程,[iOS,UI设计])