你的位置:首页 > 操作系统

[操作系统]iOS学习之UICollectionView

一、什么是集合视图

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

  • // 设置多少个分区- (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 <UICollectionViewDelegate>@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 ()<UICollectionViewDataSource, UICollectionViewDelegate, WaterFlowLayoutDelegate>// [email protected] (nonatomic, strong) NSMutableArray *allDataArr;// [email protected] (nonatomic, strong) UICollectionView *collectionView;@[email protected] 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;[email protected]

      在WaterFlowLayout.h

    #import <UIKit/UIKit.h>// [email protected] WaterFlowLayoutDelegate <NSObject>// 返回每一个item的高度- (CGFloat)heightForItemAtIndexPath:(NSIndexPath *)indexpath;@[email protected] WaterFlowLayout : UICollectionViewLayout// item的大小,[email protected] (nonatomic, assign) CGSize itemSize;// [email protected] (nonatomic, assign) UIEdgeInsets sectionInsets;// item的间距(这里水平方向和垂直方向间距一样)@property (nonatomic, assign) CGFloat spacing;// [email protected] (nonatomic, assign) NSInteger numberOfColumn;// 设置代理,[email protected] (nonatomic, weak) id<WaterFlowLayoutDelegate> delegate;@end

      在WaterFlowLayout.m

    #import "WaterFlowLayout.h"@interface WaterFlowLayout ()// 声明私有属性// [email protected] (nonatomic, assign) NSInteger numberOfItems;// [email protected] (nonatomic, strong) NSMutableArray *itemAttributes;// [email protected] (nonatomic, strong) NSMutableArray *columnHeights;// 声明私有方法// 找到当前最长列- (NSInteger)indexOflLongestColumn;// 找到当前最短列- (NSInteger)indexOfShortestColumn;@[email protected] 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<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{  return self.itemAttributes;[email protected]

      在RootCell.h

    #import <UIKit/UIKit.h>@interface RootCell : [email protected] (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;[email protected]

      在DataModel.h

    #import <Foundation/Foundation.h>@interface DataModel : NSObject// [email protected] (nonatomic, copy) NSString *thumbURL;// [email protected] (nonatomic, assign) float width;// [email protected] (nonatomic, assign) float height;@end

      在DataModel.m

    #import "DataModel.h"@implementation DataModel// 防止找不到对应key值程序崩溃- (void)setValue:(id)value forUndefinedKey:(NSString *)key{  [email protected]

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