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

[操作系统]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>// 声明大数组存放所有的数据@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 <UIKit/UIKit.h>// 声明协议@protocol WaterFlowLayoutDelegate <NSObject>// 返回每一个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<WaterFlowLayoutDelegate> 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<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{  return self.itemAttributes;}@end

      在RootCell.h

    #import <UIKit/UIKit.h>@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 <Foundation/Foundation.h>@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

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

    塞舌尔蜜月旅游暑假去塞舌尔旅游价格塞舌尔旅游6天塞舌尔旅游费用多少塞舌尔旅游哪里好玩秦岭九大魅力古镇 后柳古镇 北京香山公园门票是多少? 深圳海洋世界的门票是多少? 深圳园博园怎么去? 九道谷漂流图片?三水九道谷漂流怎么样? 九道谷漂流在南丹山吗?三水九道谷漂流在哪里? 九道谷漂流好玩吗?三水九道谷漂流刺激吗? 九道谷漂流门票预订电话?三水九道谷漂流预订优惠吗? 康琦赛介绍?湛江康琦赛简介? 齐河海底世界门票可以预订吗?齐河泉城海底世界订票电话? 康琦赛欢乐世界几点开门?湛江康琦赛欢乐世界开门时间? 余姚阳明温泉简介?浙江余姚阳明温泉介绍? 2015暑假去哪里最好玩? 海南三亚湾怎么样?适合游泳吗? 什么季节去云南最好玩? 厦门海天堂构怎么去?景观怎样? MS1503 Datasheet MS1503 Datasheet MS1504 Datasheet MS1504 Datasheet MS1579 Datasheet MS1579 Datasheet 新疆旅游 报价 新疆旅游 报价 新疆旅游 报价 深圳云南四日游价格 深圳云南四日游价格 深圳云南四日游价格 深圳出发云南旅游 深圳出发云南旅游 深圳出发云南旅游