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

[操作系统]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]

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

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