iOS学习之UICollectionView

一、什么是集合视图

在iOS6.0之后,苹果推出了一个新的继承于UIScrollView的一个视图,UICollectionView,也被称之为集合视图。

图例:

 

二、创建UICollectionView

  1、UICollectionView跟tableView实现的不同:UICollectionView的Item的布局复杂一点,需要用UICollectionViewLayout类来描述视图的布局,常用的是系统提供的UICollectionViewFlowLayout类,也可以自定义UICollectionViewLayout。

  2、创建UICollectionViewFlowLayout

   // 1.定义collectionView的样式

    self.myFlowLayout = [[UICollectionViewFlowLayout alloc] init];

    // 设置属性

    // 给定Item的大小(单元格)

    self.myFlowLayout.itemSize = CGSizeMake((self.bounds.size.width - 40) / 3, (self.bounds.size.width - 40) / 3);

    // 每两个Item的最小间隙(垂直滚动)

    self.myFlowLayout.minimumInteritemSpacing = 10;

    // 每两个Item的最小间隙(水平滚动方向)

    self.myFlowLayout.minimumLineSpacing = 10;

    // 设置滚动方向(Vertical垂直方向,horizontal水平方向)

    self.myFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;

    // 设置视图的内边距(逆时针顺序)

    self.myFlowLayout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);

    // 设置头视图,尾视图的大小(宽度不起作用,默认视图宽等屏幕宽)

    self.myFlowLayout.headerReferenceSize = CGSizeMake(0, 50);

    self.myFlowLayout.footerReferenceSize = CGSizeMake(0, 50);

  3、初始化UICollectionView

    // 2.布局collectionView

    // 创建对象,并指定样式

    self.collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:self.myFlowLayout];

  4、设置代理,遵守协议

    // 遵守协议

    @interface RootViewController ()

    @end

    @implementation RootViewController

    // 设置代理

    self.rootView.collectionView.dataSource = self;

    self.rootView.collectionView.delegate = self;

  5、注册cell

  因为UICollectionViewCell没有提供任何子控件,所以在项目中需要自定义Item。

    // 注册cell

    [self.rootView.collectionView registerClass:[RootCell class] forCellWithReuseIdentifier:identifier_cell];

  6、实现代理方法

  UICollectionView和UITableView一样有两个必须实现的代理方法:

返回多少个Item指定每个Item的样式。

// 设置每个分区里 有几个item

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

    return 10;

}

// 返回每一个item的cell对象

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

    RootCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier_cell forIndexPath:indexPath];

    cell.backgroundColor = [UIColor lightGrayColor];  

    return cell;

}

l 可选的代理方法:

l 设置多少个分区

l 设置分区头视图和尾视图:设置的视图需要自定义,和注册

l 点击item:可以通过点击item跳转视图

    // 注册头视图,尾视图

    [self.rootView.collectionView registerClass:[HeaderReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headerView"];

    [self.rootView.collectionView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footerView"];

// 设置多少个分区

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

{

    return 3;

}

// 返回头视图和尾视图

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath

{

    // 判断头视图还是尾视图

    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {

        HeaderReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"headerView" forIndexPath:indexPath];

        headerView.backgroundColor = [UIColor redColor];

        return headerView;

    }else {

        UICollectionReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"footerView" forIndexPath:indexPath];

        footerView.backgroundColor = [UIColor greenColor];

        return footerView;

    }

}

// 点击Item

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath

{

    SecondViewController *secondVC = [[SecondViewController alloc] init];

    [self.navigationController pushViewController:secondVC animated:YES];

}

  7、完成效果

 

三、布局协议

布局协议:UICollectionViewDelegateFlowLayout,它是对UICollectionViewDelegate的扩展

@protocol UICollectionViewDelegateFlowLayout

@optional

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
     - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section;
     - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section;
     - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section;
     - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;
     - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section;

@end

四、自定义UICollectionViewLayout

系统提供的UICollectionViewFlowLayout布局类能实现的效果有限,如果我们有自己想要实现的效果,就需要自定义一个UICollectionViewLayout类。下面给大家举一个实现瀑布流效果的自定义布局:

  在AppDelegate.m

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    // 创建UIWindow对象

    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];

    // 设置背景颜色

    self.window.backgroundColor = [UIColor whiteColor];

    // 使window显示    [self.window makeKeyAndVisible];

    // 创建视图控制器,给window指定根控制器

    self.window.rootViewController = [[RootViewController alloc] init];

    return YES;

}

  在RootViewController.m

#import "RootViewController.h"#import "DataModel.h"#import "WaterFlowLayout.h"#import "RootCell.h"#import "UIImageView+WebCache.h"@interface RootViewController ()// 声明大数组存放所有的数据

@property (nonatomic, strong) NSMutableArray *allDataArr;// 定义collectionView

@property (nonatomic, strong) UICollectionView *collectionView;

@end

@implementation RootViewController// 定义全局静态变量static NSString * const identifier_cell = @"identifier_cell";

// 懒加载

-(NSMutableArray *)allDataArr

{

    if (!_allDataArr) {

        _allDataArr = [NSMutableArray array];

    }

    return _allDataArr;

}- (void)viewDidLoad {

    [super viewDidLoad];

    // 读取数据    [self loadData];

    // 初始化布局    [self initLayout];

    // 注册cell

    [self.collectionView registerClass:[RootCell class] forCellWithReuseIdentifier:identifier_cell];

}

// 初始化布局

- (void)initLayout

{

    // 创建UICollectionView的样式布局

    WaterFlowLayout *water = [[WaterFlowLayout alloc] init];

    CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3;

    water.itemSize = CGSizeMake(width, width);

    // 设置内边距

    water.sectionInsets = UIEdgeInsetsMake(10, 10, 10, 10);

    // 设置间距

    water.spacing = 10;

    // 设置有多少列

    water.numberOfColumn = 3;

    // 设置代理

    water.delegate = self;

    // 布局UICollectionView

    self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:water];

    self.collectionView.delegate = self;

    self.collectionView.dataSource = self;

    self.collectionView.backgroundColor = [UIColor whiteColor];

    [self.view addSubview:self.collectionView];

}
// 读取数据

- (void)loadData

{

    // 第一步:获取文件路径

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"Data" ofType:@"json"];

    // 第二步:根据路径读取数据,转为NSData对象

    NSData *data = [NSData dataWithContentsOfFile:filePath];

    // 第三步:根据json格式解析数据

    NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];

    // 第四步:遍历数据,将数据转化成model对象

    for (NSDictionary *dict in array) {

        DataModel *dataModel = [[DataModel alloc] init];

        [dataModel setValuesForKeysWithDictionary:dict];

        [self.allDataArr addObject:dataModel];

    }//    NSLog(@"%@", self.allDataArr);    

}// 返回多少个分区

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

{

    return 1;

}

// 设置每个分区的item个数

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

    return self.allDataArr.count;

}

// 返回每一个item的cell对象

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

    RootCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier_cell forIndexPath:indexPath];

    // 设置cell数据

    DataModel *model = self.allDataArr[indexPath.row];

    [cell.photoView sd_setImageWithURL:[NSURL URLWithString:model.thumbURL]];

    cell.backgroundColor = [UIColor orangeColor];

    return cell;

}

// 实现代理方法 返回每一个item的高度

- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexpath

{

    DataModel *model = self.allDataArr[indexpath.row];

    CGFloat width = ([UIScreen mainScreen].bounds.size.width - 40) / 3;

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

    return height;

}

@end

  在WaterFlowLayout.h

#import // 声明协议@protocol WaterFlowLayoutDelegate // 返回每一个item的高度

- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexpath;

@end

@interface WaterFlowLayout : UICollectionViewLayout// item的大小,需要根据这个获取宽度
       @property (nonatomic, assign) CGSize itemSize;// 内边距的设置
       @property (nonatomic, assign) UIEdgeInsets sectionInsets;// item的间距(这里水平方向和垂直方向间距一样)
       @property (nonatomic, assign) CGFloat spacing;// 列数
       @property (nonatomic, assign) NSInteger numberOfColumn;// 设置代理,用于获取每个Item的高度

@property (nonatomic, weak) id delegate;

@end

  在WaterFlowLayout.m

#import "WaterFlowLayout.h"@interface WaterFlowLayout ()

// 声明私有属性

// 一共有多少个item@property (nonatomic, assign) NSInteger numberOfItems;

// 保存计算好的每一个item的信息

@property (nonatomic, strong) NSMutableArray *itemAttributes;

// 保存每一列的高度

@property (nonatomic, strong) NSMutableArray *columnHeights;

// 声明私有方法// 找到当前最长列

- (NSInteger)indexOflLongestColumn;

// 找到当前最短列

- (NSInteger)indexOfShortestColumn;

@end

@implementation WaterFlowLayout

// 懒加载

- (NSMutableArray *)itemAttributes

{

    if (!_itemAttributes) {

        _itemAttributes = [NSMutableArray array];

    }

    return _itemAttributes;

}

- (NSMutableArray *)columnHeights

{

    if (!_columnHeights) {

        _columnHeights = [NSMutableArray array];

    }

    return _columnHeights;

}

// 找到最长列

- (NSInteger)indexOflLongestColumn

{

    // 记录最长的下标

    NSInteger index = 0;

    // 记录最长列的高度

    CGFloat length = 0;

    for (int i = 0; i < self.columnHeights.count; i++) {

        // 将数组中的对象转为基本数值

        CGFloat currentLength = [self.columnHeights[i] floatValue];

        if (currentLength > length) {

            length = currentLength;

            index = i;

        }

    }

    return index;

}

// 找到最短列

- (NSInteger)indexOfShortestColumn

{

    // 记录最短的下标

    NSInteger index = 0;

    // 记录最短列的高度

    CGFloat length = [self.columnHeights[0] floatValue];

    for (int i = 0; i < self.columnHeights.count; i++) {

        // 将数组中的对象转为基本数值

        CGFloat currentLength = [self.columnHeights[i] floatValue];

        if (currentLength < length) {

            length = currentLength;

            index = i;

        }

    }

    return index;

}#pragma mark 重写Layout布局都会走这3个方法// 重写三个方法// 准备布局,在这里计算每个item的frame

- (void)prepareLayout

{

    // 拿到一个有多少个item

    self.numberOfItems = [self.collectionView numberOfItemsInSection:0];

    // 每一列添加一个top高度

    for (int i = 0; i < self.numberOfColumn; i++) {

        // @() NSNumber字面量创建对象

        self.columnHeights[i] = @(self.sectionInsets.top);

    }

    // 依次为每个item设置位置信息,并存储在数组中

    for (int i = 0; i < self.numberOfItems; i++) {

        // 找到最短列下标

        NSInteger shortestIndex = [self indexOfShortestColumn];

        // 计算x 目标x = 内编剧做间距 + (宽 +item间距)* 最短列下标

        CGFloat detalX = self.sectionInsets.left + shortestIndex * (self.itemSize.width + self.spacing);

        // 找到最短列的高度

        CGFloat height = [self.columnHeights[shortestIndex] floatValue];

        // 计算Y

        CGFloat detalY = height + self.spacing;

        // 创建indexPath

        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];

        // 调用代理方法计算高度

        CGFloat itemHeight = 0;

        if (_delegate && [_delegate respondsToSelector:@selector(heightForItemAtIndexPath:)]) {

            itemHeight = [_delegate heightForItemAtIndexPath:indexPath];

        }

        // 定义保存位置信息的对象

        UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

        // 生成frame

        attributes.frame = CGRectMake(detalX, detalY, self.itemSize.width, itemHeight);

        // 将位置信息添加到数组中        [self.itemAttributes addObject:attributes];

        // 更新这一列的高度

        self.columnHeights[shortestIndex] = @(detalY + itemHeight);

    }

}// 返回UICollectionView的大小

- (CGSize)collectionViewContentSize

{

    // 求最高列的下标

    NSInteger longest = [self indexOflLongestColumn];

    // 最高列的高度

    CGFloat height = [self.columnHeights[longest] floatValue];

    // 拿到collectionView的原始大小

    CGSize size = self.collectionView.frame.size;

    size.height = height + self.sectionInsets.bottom;

    return size;

}// 返回每一个Item的信息

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect

{

    return self.itemAttributes;

}

@end

  在RootCell.h

#import

@interface RootCell : UICollectionViewCell

@property (nonatomic, strong) UIImageView *photoView;

@end

  在RootCell.m

#import "RootCell.h"

@implementation RootCell

- (instancetype)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self) {

        self.photoView = [[UIImageView alloc] init];

        [self.contentView addSubview:self.photoView];

    }

    return self;

}

// 子布局改变

- (void)layoutSubviews

{

    self.photoView.frame = self.bounds;

}

@end

  在DataModel.h

#import

@interface DataModel : NSObject// json文件图片地址

@property (nonatomic, copy) NSString *thumbURL;// 图片宽

@property (nonatomic, assign) float width;// 图片高

@property (nonatomic, assign) float height;

@end

  在DataModel.m

#import "DataModel.h"

@implementation DataModel

// 防止找不到对应key值程序崩溃

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

{

}

@end

  程序运行效果(瀑布流效果):

 

the end

评论(0)