UICollectionViewController 集合视图控制器

本文深入探讨了UICollectionViewController类及其与UICollectionView的整合封装,详细介绍了UICollectionViewController的基本属性、代理、Cell、布局等内容,并提供了实现流程及自定义实例,帮助开发者更好地理解和运用这一强大的数据展示方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述: 
    UICollectionViewController 类似于 UITableViewController 是UICollectionView 与 控制器类的整合封装.其作用也就主要体现在UICollocationView

UICollectionViewController 基本属性

- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
@property(nonatomic, retain) UICollectionView *collectionView
@property(nonatomic, readonly) UICollectionViewLayout *collectionViewLayout
@property(nonatomic) BOOL clearsSelectionOnViewWillAppear
@property(nonatomic, assign) BOOL useLayoutToLayoutNavigationTransitions

UICollectionView: 是一种新的数据展示方式, 简单来说可以把他理解成多列的UITableView. 
UICollectionView概述: 
标准的 UICollectionView 包含三个部分,他们都是UIView的子类:
  • Cells 用于展示内容的主体,对于不同的cell可以指定不同尺寸和不同的内容,这个稍后再说
  • Supplementary Views 追加视图 如果你对UITableView比较熟悉的话,可以理解为每个Section的Header或者Footer,用来标记每个section的view
  • Decoration Views 装饰视图 这是每个section的背景,比如iBooks中的书架就是这个----不管一个UICollectionView的布局如何变化,这三个部件都是存在的。

关于 UICollectionView 的实现:  
 实现一个UICollectionView 和实现一个UITableView基本上没有什么大的区别. 
    他们都是同样的datasource 和 delegate 设计模式: datasource为view提供资源, 告诉view要显示些什么以及如何显示他们, delegate提供一些样式的小细节以及用户交互的响应.
    同样有三个必须实现的协议方法:通过协议方法进行两个视图子类(cells/ supplementary views)的展示.
    需要注意的是, 对于Decoration views, 提供方法并不在UIControllerViewDataSource中, 而是直接在UICollectionViewLayout类中的(因为它仅仅于视图相关, 而与数据无关).
    同样具有重用机制. 但是需要注意的时,在UICollectionView中, 不仅cell可以重用, Supplement View 和 Decoration view 也可以并且应该被重用的. 
关于 UICollectionView 的代理:
    UICollectionViewDelegate: View的外形, 用户交互等由UICollectionViewDelegate来负责, 而数据的提供则有UICollectionViewDataSource负责.
关于Cell: 
    相对于UITableViewCell, UICollectionViewCell则相对简单,这主要是由于展示对象的性质决定的, 因为UICollectionView展示的对象更为灵活,因此需求也就千奇百怪. 因此SDK提供给我们的默认UICollectionViewCell在结构上比较简单, 由上至下:  cell本身的View/ backgroundView/ selectedBackgroundView/ contentView.
UICollectionViewLayout:
    UICollectionView的精髓.可以通过对其子类化实现对单元格的自定义布局, 这也就是UICollectionView 和 UITableView的最大区别了. UICollectionViewLayout可以说时UICollectionView的大脑和中枢, 它负责cell Supplementary View 和 Decoration Views 进行组织, 为他们设定各自的属性, 包括但不限于: 位置/ 尺寸/ 透明度/ 层级关系/ 形状/ 等等....
    Apple 为我们提供了一个最简答可能也是最常用的默认的layout对象, UICollectionViewFlowLayout. Flow Layout 简单说就是一个直线对其的layout. 通过对这个子类的子类化可以实现大部分的瀑布流类的简单实现.
总结:
    总的来说, 一个UICollectionView的实现必须包含两个必要的部分: UICollectionViewDataSource 和 UICollectionViewLayout分别提供展示的View内容和 展示的View的位置. 和一个交互部分:  UICollectionViewDelegate. 而 Apple给出的UICollectionViewFlowLayout已经是一个很强力的layout方案了.但是仅仅如此还是不够的. UICollectionView的强大之处, 就在于各种layout的自定义实现, 以及他们之间的切换.  

1. UICollectionView
    1.1 基本属性/方法

@property(nonatomic, assign) id< UICollectionViewDelegate > delegate
@property(nonatomic, assign) id< UICollectionViewDataSource > dataSource
代理
@property(nonatomic, retain) UIView *backgroundView
背景视图
- (void)registerClass:(Class)viewClass
forSupplementaryViewOfKind:(NSString *)elementKind
  withReuseIdentifier:(NSString *)identifier
- (id)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier
                                forIndexPath:(NSIndexPath *)indexPath
重用方法

@property(nonatomic, retain) UICollectionViewLayout *collectionViewLayout
- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout
                       animated:(BOOL)animated
切换布局
- (void)reloadData
- (void)reloadSections:(NSIndexSet *)sections
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
数据更新
- (NSInteger)numberOfSections
- (NSInteger)numberOfItemsInSection:(NSInteger)section
- (NSArray *)visibleCells
获取内容数量
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath
                toIndexPath:(NSIndexPath *)newIndexPath
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths

添加/移动/删除items(类似tableView的增删改)
- (void)insertSections:(NSIndexSet *)sections
- (void)deleteSections:(NSIndexSet *)sections
- (void)moveSection:(NSInteger)section
          toSection:(NSInteger)newSection
添加/删除/移动sections
@property(nonatomic) BOOL allowsSelection
@property(nonatomic) BOOL allowsMultipleSelection
- (NSArray *)indexPathsForSelectedItems
- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath
                     animated:(BOOL)animated
               scrollPosition:(UICollectionViewScrollPosition)scrollPosition
- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath
                       animated:(BOOL)animated

是否可选
- (NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point
- (NSArray *)indexPathsForVisibleItems
- (NSIndexPath *)indexPathForCell:(UICollectionViewCell *)cell
- (UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath
对item/section进行定位
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
- (UICollectionViewLayoutAttributes*)layoutAttributesForSupplementaryElementOfKind:(NSString *)kind
                                                                        atIndexPath:(NSIndexPath *)indexPath
获取布局信息
- (void)performBatchUpdates:(void (^)(void))updates
                 completion:(void (^)(BOOL finished))completion
多点选择 的删除于移动

1.2 常用操作
添加/删除/移动/编辑

1.3 UICollectionViewDataSource/ UICollectionViewDelegate 协议

 UICollectionViewDataSource  
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
didDeselectItemAtIndexPath:(NSIndexPath *)indexPath

选中状态历程
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
       willDisplayCell:(UICollectionViewCell *)cell
    forItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
willDisplaySupplementaryView:(UICollectionReusableView *)view
        forElementKind:(NSString *)elementKind
           atIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
  didEndDisplayingCell:(UICollectionViewCell *)cell
    forItemAtIndexPath:(NSIndexPath *)indexPath
- (void)collectionView:(UICollectionView *)collectionView
didEndDisplayingSupplementaryView:(UICollectionReusableView *)view
      forElementOfKind:(NSString *)elementKind
           atIndexPath:(NSIndexPath *)indexPath
监控单元格的加载于移除
- (UICollectionViewTransitionLayout *)collectionView:(UICollectionView*)collectionView
                        transitionLayoutForOldLayout:(UICollectionViewLayout*)fromLayout
                                           newLayout:(UICollectionViewLayout*)toLayout
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath  


编辑菜单
- (BOOL)collectionView:(UICollectionView *)collectionView
      canPerformAction:(SEL)action
    forItemAtIndexPath:(NSIndexPath *)indexPath
            withSender:(id)sender

 - (void)collectionView:(UICollectionView *)collectionView
         performAction:(SEL)action
    forItemAtIndexPath:(NSIndexPath *)indexPath
            withSender:(id)sender
当编辑菜单存在时可用
   
   


1.4 实例
//1 创建用于布局单元格的布局对象
   
UICollectionViewFlowLayout *flowLayout1 = [[UICollectionViewFlowLayoutalloc]init];
   
//2 设置最小行间距
    flowLayout1.
minimumInteritemSpacing= 5;
   
//3 设置最小列间距
    flowLayout1.
minimumLineSpacing= 5;
   
//4 设置单元格的大小
    flowLayout1.
itemSize = CGSizeMake((CGRectGetWidth(self.view.bounds) - 15) /2 , (CGRectGetWidth(self.view.bounds) - 15) /2);
   
//5 设置布局时的内边距
    flowLayout1.
sectionInset = UIEdgeInsetsMake(5,5,5,5);
   
   
   
   
//使用自定义的layout布局
   
   
WaterFlowLayout *flowLayout = [[WaterFlowLayout alloc] init];
    flowLayout.
columnCount = 2;//表示有两列
    flowLayout.
delegate = self;
    flowLayout.
sectionInset = UIEdgeInsetsMake(10,10,10,10);
    flowLayout.
itemWitdth = (CGRectGetWidth(self.view.bounds) - 30 ) / 2;
   
   
   
   
UICollectionView *collectionView = [[UICollectionViewalloc]initWithFrame:self.view.boundscollectionViewLayout:flowLayout];
   
    collectionView.
tag = 1001;
   
   
   
//设置背景颜色
    collectionView.
backgroundColor= [UIColorwhiteColor];
   
//设置代理
    collectionView.
delegate = self;
    collectionView.
dataSource = self;
    [
self.viewaddSubview:collectionView];
    [collectionView
release];
    [flowLayout
release];
   
   
//为对应重用标示注册单元格类型
    [collectionView registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:@"CELL"];
//执行协议方法
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)collectionView {
   
return 1;
}

//返回制定分区的单元格数量
- (
NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section {

   
return self.datasource.count;
}
//返回对应行的单元格对象
- (
UICollectionViewCell *)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath {
 
  
ImageCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CELL"forIndexPath:indexPath];
    cell.
backgroundColor = [UIColor colorWithRed:arc4random() %256 / 255.0 green:arc4random() %256 / 255.0 blue:arc4random() %256 / 255.0 alpha:1];
   
   
ImageModel *model = self.datasource[indexPath.item];
    [cell.
imageView setImageWithURL:[NSURLURLWithString:model.thumbURL]];
 
   
   
return cell;
}




2. UICollectionViewLayout

    集合视图控制器布局对象完成了对集合视图控制器对象单元格frame的布局,所以其核心即单元格frame的布局,最终要求即获得一个单元格属性模型数组.
    2.1 基本属性

@property(nonatomic, readonly) UICollectionView *collectionView
- (CGSize)collectionViewContentSize
当前使用此布局对象的集合视图
同时还提供了相应的cell布局的切换/ cell的增/删/移动

    2.2 自定义
自定义布局时, 必须要重写的方法如下:
      prepareLayout (即将开始布局时调用.重写此方法, 实现在即将布局时,获取属性模型数组)
    collectionViewContentSize(重写此方法返回collectionView的contentSize)
    layoutAttributesForSupplementaryViewOfKind:atIndexPath: (if your layout supports supplementary views)
    layoutAttributesForDecorationViewOfKind:atIndexPath: (if your layout supports decoration views)
    shouldInvalidateLayoutForBoundsChange:

如果对应的collection需要实现增加/删除/移动功能, 则需要重写以下方法
  •   initialLayoutAttributesForAppearingItemAtIndexPath:

  •   initialLayoutAttributesForAppearingSupplementaryElementOfKind:atIndexPath:

  •   initialLayoutAttributesForAppearingDecorationElementOfKind:atIndexPath:

  •   finalLayoutAttributesForDisappearingItemAtIndexPath:

  •   finalLayoutAttributesForDisappearingSupplementaryElementOfKind:atIndexPath:

  •   finalLayoutAttributesForDisappearingDecorationElementOfKind:atIndexPath:
  • 另外视图布局对象也提供了一些方法用于提升性能

  UICollectionViewLayout的自定义大概可以分为一下几步:
    1> 根据需求扩展UICollectionView的协议方法
    2> 定义需求的属性信息(因为UICollectionViewLayout基类的属性)
    3> 获取cell的的属性模型数组(自定义的核心) 
    4> 重写相应的父类方法(基本状态下有: )

    2.4: 自定义实例(瀑布流)

#import <UIKit/UIKit.h>

@class WaterFlowLayout;
//声明扩展协议
@protocol WaterFlowLayoutDelegate <UICollectionViewDelegate>
//为布局对象返回单元格的最终高度
- (
CGFloat)collectionView:(UICollectionView*)collectionView layout:(WaterFlowLayout*)waterFlowLayout heightyForItemAtIndexPath:(NSIndexPath*)indexPath;

@end


@interface WaterFlowLayout : UICollectionViewLayout

@property (nonatomic,assign)id<WaterFlowLayoutDelegate>delegate;//代理对象属性
@property (nonatomic,assign)NSUInteger columnCount;//设置瀑布流的列数
@property (nonatomic,assign)CGFloat itemWitdth; //设置瀑布流中每个单元格的宽度
@property (nonatomic,assign)UIEdgeInsets sectionInset;//设置瀑布流的内边距

@end
#import"WaterFlowLayout.h"

@interfaceWaterFlowLayout ()

@property(nonatomic,assign)NSUInteger numberOfItems;//总的单元格数量
@property (nonatomic,assign)CGFloat interItemSpacing;//列间距
@property (nonatomic,retain)NSMutableArray *columnHeights;//保存布局时当前每一列的当前总高度. 有多少列,就有多少个数组元素
@property (nonatomic,retain)NSMutableArray *itemAttributes;//用于保存每一个单元格的属性模型对象.
- (NSInteger)shortestColumnIndex;//返回columnHeights数组中当前最短列下标
- (NSInteger)longestColumnIndex;//返回columnHeightss数组中当前最长列下标

@end

@implementationWaterFlowLayout

- (
void)dealloc
{
    [
_columnHeights release];
    [
_itemAttributes release];
    [
super dealloc];
}

- (
NSMutableArray *)columnHeights
{
   
if (!_columnHeights) {
       
self.columnHeights= [NSMutableArrayarray];
    }
   
return _columnHeights;
}

- (
NSMutableArray *)itemAttributes
{
   
if (!_itemAttributes) {
       
self.itemAttributes= [NSMutableArrayarray];
    }
   
return _itemAttributes;
}

- (
NSInteger)shortestColumnIndex
{
   
NSInteger index = 0;
   
CGFloat shortestHeight = CGFLOAT_MAX;//浮点数的最大值
   
for (int i = 0 ; i < self.columnHeights.count;i++) {
       
CGFloat currentHeight = [self.columnHeights[i]doubleValue];
       
if (currentHeight < shortestHeight) {
            index = i;
            shortestHeight = currentHeight;
        }
    }
   
return index;
}

- (
NSInteger)longestColumnIndex
{
   
NSInteger indext = 0;
   
CGFloat longestHeight = 0;
   
for (int i = 0; i <self.columnHeights.count; i ++) {
       
CGFloat currentHeight = [self.columnHeights[i]doubleValue];
       
if (currentHeight > longestHeight) {
            indext = i;
            longestHeight = currentHeight;
        }
    }
   
return indext;
}

- (
void)layoutItems
{
   
//获取单元格的总数量
   
self.numberOfItems= [self.collectionViewnumberOfItemsInSection:0];
   
//计算内容区域的最终宽度
   
CGFloat contentWidth = CGRectGetWidth(self.collectionView.bounds) - self.sectionInset.left- self.sectionInset.right;
   
//计算列边距
   
self.interItemSpacing= (contentWidth - self.columnCount* self.itemWitdth) / (self.columnCount- 1);
   
//先通过列数为columnHeight 添加当前高度
   
for (int i = 0 ; i < self.columnCount; i ++) {
       
//语法糖  自动转数据为对象类型
       
self.columnHeights[i] =@(self.sectionInset.top);
    }
   
   
for (int i = 0 ; i < self.numberOfItems; i ++) {
       
//创建单元格对应的indexPath对象
       
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
       
//通过代理对象得到单元格的最终高度
       
CGFloat itemHeight = [self.delegatecollectionView:self.collectionViewlayout:selfheightyForItemAtIndexPath:indexPath];
       
       
//获取当前最小列的下标
       
NSInteger shortestIndex = [self shortestColumnIndex];
       
//计算单元格的x, y 轴坐标
       
CGFloat x = self.sectionInset.left+ (self.itemWitdth  +self.interItemSpacing) * shortestIndex ;
       
CGFloat y = [self.columnHeights[shortestIndex]doubleValue] ;
       
//创建属性模型对象
       
UICollectionViewLayoutAttributes*attributes = [UICollectionViewLayoutAttributeslayoutAttributesForCellWithIndexPath:indexPath];
       
//设置frame
        attributes.
frame = CGRectMake(x, y,self.itemWitdth, itemHeight);
       
//保存在属性数组中
        [
self.itemAttributesaddObject:attributes];
       
       
//更新当前列(最短列)的总高度
       
self.columnHeights[shortestIndex] =@(y + itemHeight +self.interItemSpacing);
       
NSLog(@"%@",NSStringFromCGRect(attributes.frame));
    }

}


- (
void)prepareLayout
{
    [
super prepareLayout];
   
//在准备布局时调用此方法计算每个单元格的frame
    [
self layoutItems];
   
}

- (
NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
   
//为当前区域返回对应保存属性模型的数组
   
return self.itemAttributes;
}

- (
UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath*)indexPath
{
   
//为每个单元格分发对应的属性模型的对象
   
return self.itemAttributes[indexPath.item];
}

- (
CGSize)collectionViewContentSize
{
   
CGSize contentSize = self.collectionView.frame.size;
   
//获取最长列的下标
   
NSInteger longestIndex = [self longestColumnIndex];
    contentSize.
height = [self.columnHeights[longestIndex]doubleValue];
   
return contentSize;
}

@end
    

    2.3 UICollectionViewFlowLayout
提供了一个简单的集合视图布局, 能够实现自定义行数的简单布局. 通过itemSize/ 行间距/ 列间距/ 内边距实现单的布局
3. UICollectionViewCell
只具有四个五个属性提供了自定义空间(contentView/backgroundView/selectedBackgroundView) 和选择状态(selected/ highlighted)

4. UICollectionViewLayoutAttributes
用于保存属性的模型类,适用于UICollectionViewLayout的初始化方法为 +(id)layoutAttributesForCellWithIndexPath:(NSIndexPath *) indexPath
可以保存的属性主要有: frame/bounds/center/size/transform3D/transform/alpha/zIndex(堆叠顺序)/hidden


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值