UICollctionViewLayout 介绍
使用UITableView时,其有两种样式
typedef NS_ENUM(NSInteger, UITableViewStyle) {
UITableViewStylePlain, // regular table view
UITableViewStyleGrouped // preferences style table view
};
而UICollectionView 通过,UICollectionViewLayout来设计样式.
注意:
UICollectionViewLayout是抽象类,使用时必须要子类化.不然除了UICollectionViewDelegate和UICollectionViewLayoutDelegate不能执行外,部分UICollectionViewDataSource代理方法也无法执行,完全无法达到需要的效果.
官方的类的说明如下:
// The UICollectionViewLayout class is provided as an abstract class for subclassing to define custom collection layouts.
// Defining a custom layout is an advanced operation intended for applications with complex needs.
内容部分
系统给我们设计的UICollectionView+ UICollectionViewFlowLayout 就可以完成大部分操作,完全的自定义要考虑很多内容.
//布局UICollectonVIew 子视图
@interface UICollectionViewLayout (UISubclassingHooks)
@end
// 关于删除插入操作等
@interface UICollectionViewLayout (UIUpdateSupportHooks)
@end
// 移动动作布局
@interface UICollectionViewLayout (UIReorderingSupportHooks)
@end
1. UICollectionViewLayout 属性和方法
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionViewLayout : NSObject <NSCoding>
// 初始化方法
- (instancetype)init NS_DESIGNATED_INITIALIZER;
// 初始化方法(在使用Xib或者StoryBoard时)
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
// Methods in this class are meant to be overridden and will be called by its collection view to gather layout information.
// To get the truth on the current state of the collection view, call methods on UICollectionView rather than these.
//当前的UICollectionView
@property (nullable, nonatomic, readonly) UICollectionView *collectionView;
#TODO:使布局失效:
// Call -invalidateLayout to indicate that the collection view needs to requery the layout information.
// Subclasses must always call super if they override.
// 主动使当前的布局失效,同时触发布局更新
// 重载该方法的时候,必须调用super类的该方法。
- (void)invalidateLayout;
// 使局部布局失效,同时更新这部分的布局.
- (void)invalidateLayoutWithContext:(UICollectionViewLayoutInvalidationContext *)context NS_AVAILABLE_IOS(7_0);
// 注册装饰视图
- (void)registerClass:(nullable Class)viewClass forDecorationViewOfKind:(NSString *)elementKind;
// 注册带Xib的装饰视图
- (void)registerNib:(nullable UINib *)nib forDecorationViewOfKind:(NSString *)elementKind;
@end
2. UICollectionViewLayout 的类别
2.1 UISubclassingHooks 布局UICollectionView 的子视图
@interface UICollectionViewLayout (UISubclassingHooks)
#if UIKIT_DEFINE_AS_PROPERTIES
// 返回要创建布局属性时需要的类
@property(class, nonatomic, readonly) Class layoutAttributesClass; // override this method to provide a custom class to be used when instantiating instances of UICollectionViewLayoutAttributes
// 返回在为布局创建无效上下文时使用的类
@property(class, nonatomic, readonly) Class invalidationContextClass NS_AVAILABLE_IOS(7_0); // override this method to provide a custom class to be used for invalidation contexts
#else
+ (Class)layoutAttributesClass; // override this method to provide a custom class to be used when instantiating instances of UICollectionViewLayoutAttributes
+ (Class)invalidationContextClass NS_AVAILABLE_IOS(7_0); // override this method to provide a custom class to be used for invalidation contexts
#endif
#TODO: 布局内容
#TODO:自定义布局时,一定要重载此方法
// The collection view calls -prepareLayout once at its first layout as the first message to the layout instance.
// The collection view calls -prepareLayout again after layout is invalidated and before requerying the layout information.
// Subclasses should always call super if they override.
// 告诉布局对象来更新布局
// 集合视图再调用此方法时, 当它第一次布局时,作为第一个消息发送给布局集合视图实例.
// 集合视图在布局无效后和获取布局信息前会再次调用此方法.
// 注意:如果重载次方法,必须调用其super方法.
- (void)prepareLayout;
# TODO:
// UICollectionView calls these four methods to determine the layout information.
// Implement -layoutAttributesForElementsInRect: to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
// Additionally, all layout subclasses should implement -layoutAttributesForItemAtIndexPath: to return layout attributes instances on demand for specific index paths.
// If the layout supports any supplementary or decoration view types, it should also implement the respective atIndexPath: methods for those types.
// UICollectionView 调用这四个方法去确定布局
// 实现 -layoutAttributesForElementsInRect 方法 返回装饰或者追加视图
// 返回rect内cell,追加视图和装饰视图的布局属性,存储内容是UICollectionViewLayoutAttributes的数组.
- (nullable NSArray<__kindof UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect; // return an array layout attributes instances for all the views in the given rect
// 返回指定路径上的Item的布局属性
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
// 返回指定路径上的追加视图上的布局属性
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
// 返回指定路径上的装饰视图的布局属性
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath;
// 当边界发生改变时,是否刷新布局.如果YES则在边界变化(一般是scroll到其他地方)时,将重新计算需要的布局信息。
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds; // return YES to cause the collection view to requery the layout for geometry information
// 当边界更改时,返回更改布局部分的上下文
- (UICollectionViewLayoutInvalidationContext *)invalidationContextForBoundsChange:(CGRect)newBounds NS_AVAILABLE_IOS(7_0);
// 如果自定义单元格需要更新布局,会请求布局对象
- (BOOL)shouldInvalidateLayoutForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes NS_AVAILABLE_IOS(8_0);
// 返回标识的被移动的item,布局无效上下文.
- (UICollectionViewLayoutInvalidationContext *)invalidationContextForPreferredLayoutAttributes:(UICollectionViewLayoutAttributes *)preferredAttributes withOriginalAttributes:(UICollectionViewLayoutAttributes *)originalAttributes NS_AVAILABLE_IOS(8_0);
#TODO:point
//停止滚动时,调用此方法,确定collectionView滚动到的位置
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity; // return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior
// 当进行动画式布局的时候,该方法返回内容区的偏移量
// 当更新或者布局转场的时候,collectionView调用改变内容的偏移量.该偏移量作为动画的结束点
// 如果动画或者转场造成item位置的改变并不是以最优的方式进行,可以重载该方法进行优化。 collection view在调用prepareLayout 和 collectionViewContentSize 之后调用该方法
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset NS_AVAILABLE_IOS(7_0); // a layout can return the content offset to be applied during transition or update animations
#if UIKIT_DEFINE_AS_PROPERTIES
// collectionView 内容区域
@property(nonatomic, readonly) CGSize collectionViewContentSize; // Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size to facilitate scrolling.
#else
- (CGSize)collectionViewContentSize; // Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size to facilitate scrolling.
#endif
@end
2.2 UICollectionViewLayout 类别UIUpdateSupportHooks.由于删除,插入item.
@interface UICollectionViewLayout (UIUpdateSupportHooks)
// This method is called when there is an update with deletes/inserts to the collection view.
// It will be called prior to calling the initial/final layout attribute methods below to give the layout an opportunity to do batch computations for the insertion and deletion layout attributes.
// The updateItems parameter is an array of UICollectionViewUpdateItem instances for each element that is moving to a new index path.
// 这些方法将再删除或者插入时被调用.
// 在调用初始化或者最后布局属性之前调用,去给布局一个机会进行批量的插入和删除操作.
// 更新items的元参数是为每一个元素包含内容为UICollectionViewUpdateItem 实例的数组.
#TODO: 为CollectionView 的更新的准备动画
// 通知布局对象布局内容将要改变
- (void)prepareForCollectionViewUpdates:(NSArray<UICollectionViewUpdateItem *> *)updateItems;
// 在集合视图更新期间,执行添加或者需要清除的动画
- (void)finalizeCollectionViewUpdates; // called inside an animation block after the update
#TODO: 为CollectionView 的bounds 改变的动画 调整动态的变化
// collection view 为布局对象准备因为items的bounds改变或者插入\删除操作添加的动画.在其改变前调用此方法.
- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds; // UICollectionView calls this when its bounds have changed inside an animation block before displaying cells in its new bounds
// 在视图的边界进行任何更改\插入或者删除items之后,清除相关操作
- (void)finalizeAnimatedBoundsChange; // also called inside the animation block
#TODO: 布局之间的变化
// UICollectionView calls this when prior the layout transition animation on the incoming and outgoing layout
// 告知布局对象将会作为新的布局被导入到 collection view,该方法先于转场之前执行,可以在该方法做一些初始化的操作,生成布局attributes;
- (void)prepareForTransitionToLayout:(UICollectionViewLayout *)newLayout NS_AVAILABLE_IOS(7_0);
// 告知布局对象作为布局即将从collection view移除,该方法先于转场之前执行,可以在该方法做一些初始化的操作,生成布局attributes;
- (void)prepareForTransitionFromLayout:(UICollectionViewLayout *)oldLayout NS_AVAILABLE_IOS(7_0);
//collection view在获取从一个布局向另一个布局转场的时候所有的布局attributes 后,调用该方法。你可以用该方法清空prepareForTransitionFromLayout: 和prepareForTransitionToLayout:生成的数据和缓存。
- (void)finalizeLayoutTransition NS_AVAILABLE_IOS(7_0); // called inside an animation block after the transition
#TODO: 数据变化后,更新布局信息
// 当collection view的数据发生改变的时候,比如插入或者删除 item的时候,collection view将会要求布局对象更新相应的布局信息。移动、添加、删除 items时都必须更新相应的布局信息以便反映元素最新的位置。对于移动的元素, collection view提供了标准的方法获取更新后的布局信息。而collection view删除或者添加元素的时候,将会调用一些不同的方法
// This set of methods is called when the collection view undergoes an animated transition such as a batch update block or an animated bounds change.
// For each element on screen before the invalidation, finalLayoutAttributesForDisappearingXXX will be called and an animation setup from what is on screen to those final attributes.
// For each element on screen after the invalidation, initialLayoutAttributesForAppearingXXX will be called and an animation setup from those initial attributes to what ends up on screen.
// 当集合视图进行譬如批量的更新回调的转场动画或者bounds改变动画会调用下面一些列的方法
// 屏幕中的每一个元素无效化前,finalLayoutAttributesForDisappearingXXX方法将会被调用,并且,动画设置根据最后的属性.
// 屏幕中的每一个元素无效化后,将回调用initialLayoutAttributesForAppearingXXX方法,并且动动画设置初始化到呈现到屏幕中这个过程.
//在一个 item被插入到collection view 的时候,返回开始的布局信息。这个方法在 prepareForCollectionViewUpdates:之后和finalizeCollectionViewUpdates 之前调用。collection view将会使用该布局信息作为动画的起点(结束点是该item在collection view 的最新的位置)。如果返回为nil,布局对象将用item的最终的attributes 作为动画的起点和终点。
- (nullable UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath;
//返回值是item即将从collection view移除时候的布局信息,对即将删除的item来讲,该方法在 prepareForCollectionViewUpdates: 之后和finalizeCollectionViewUpdates 之前调用。在该方法中返回的布局信息描包含 item的状态信息和位置信息。 collection view将会把该信息作为动画的终点(起点是item当前的位置)。如果返回为nil的话,布局对象将会把当前的attribute,作为动画的起点和终点。
- (nullable UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath;
//返回值是追加视图插入collection view时的布局信息。该方法使用同initialLayoutAttributesForAppearingItemAtIndexPath:
- (nullable UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)elementIndexPath;
- (nullable UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)elementIndexPath;
//返回值是装饰视图插入collection view时的布局信息。该方法使用同initialLayoutAttributesForAppearingItemAtIndexPath:
- (nullable UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingDecorationElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)decorationIndexPath;
- (nullable UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingDecorationElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)decorationIndexPath;
# TODO:
// These methods are called by collection view during an update block.
// Return an array of index paths to indicate views that the layout is deleting or inserting in response to the update.
// 在更新期间,集合视图将会调用这些方法.
// 返回表示布局视图正在更新或者插入等更新请求操作的索引路径数组.
// 返回表示要删除的补充视图的索引路径数组。
- (NSArray<NSIndexPath *> *)indexPathsToDeleteForSupplementaryViewOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(7_0);
// 返回表示要删除的装饰视图的索引路径数组。
- (NSArray<NSIndexPath *> *)indexPathsToDeleteForDecorationViewOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(7_0);
// 返回表示要插入的追加视图的索引路径数组。
- (NSArray<NSIndexPath *> *)indexPathsToInsertForSupplementaryViewOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(7_0);
// 返回表示要插入的补充视图的索引路径数组。
- (NSArray<NSIndexPath *> *)indexPathsToInsertForDecorationViewOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(7_0);
@end
2.3 UICollectionViewLayout 类别
UIReorderingSupportHooks 移动动作布局
@interface UICollectionViewLayout (UIReorderingSupportHooks)
// 返回的是索引路径.两个参数分别为原先的索引路径和之前集合视图的位置. 在item移动的过程中,该方法将UICollectionView中的location映射成相应 NSIndexPaths。该方法的默认是显示,查找指定位置的已经存在的cell,返回该cell的NSIndexPaths 。如果在相同的位置有多个cell,该方法默认返回最上层的cell。
- (NSIndexPath *)targetIndexPathForInteractivelyMovingItem:(NSIndexPath *)previousIndexPath withPosition:(CGPoint)position NS_AVAILABLE_IOS(9_0);
// 当item在手势交互下移动时,通过该方法返回这个item布局的attributes 。
//默认实现是,复制已存在的attributes,改变attributes两个值,一个是中心点center;另一个是z轴的坐标值,设置成最大值。所以该item在collection view的最上层。子类重载该方法,可以按照自己的需求更改attributes,首先需要调用super类获取attributes,然后自定义返回的布局属性.
- (UICollectionViewLayoutAttributes *)layoutAttributesForInteractivelyMovingItemAtIndexPath:(NSIndexPath *)indexPath withTargetPosition:(CGPoint)position NS_AVAILABLE_IOS(9_0);
- (UICollectionViewLayoutInvalidationContext *)invalidationContextForInteractivelyMovingItems:(NSArray<NSIndexPath *> *)targetIndexPaths withTargetPosition:(CGPoint)targetPosition previousIndexPaths:(NSArray<NSIndexPath *> *)previousIndexPaths previousPosition:(CGPoint)previousPosition NS_AVAILABLE_IOS(9_0);
- (UICollectionViewLayoutInvalidationContext *)invalidationContextForEndingInteractiveMovementOfItemsToFinalIndexPaths:(NSArray<NSIndexPath *> *)indexPaths previousIndexPaths:(NSArray<NSIndexPath *> *)previousIndexPaths movementCancelled:(BOOL)movementCancelled NS_AVAILABLE_IOS(9_0);
@end
3. UICollectionViewLayoutAttributes
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionViewLayoutAttributes : NSObject <NSCopying, UIDynamicItem>
@property (nonatomic) CGRect frame; // 框架
@property (nonatomic) CGPoint center; //中心坐标
@property (nonatomic) CGSize size; // 尺寸
@property (nonatomic) CATransform3D transform3D; // 放射变幻
@property (nonatomic) CGRect bounds NS_AVAILABLE_IOS(7_0); // 范围
@property (nonatomic) CGAffineTransform transform NS_AVAILABLE_IOS(7_0);
@property (nonatomic) CGFloat alpha; //透明度
@property (nonatomic) NSInteger zIndex; // default is 0 // 视图层级
@property (nonatomic, getter=isHidden) BOOL hidden; // As an optimization, UICollectionView might not create a view for items whose hidden attribute is YES //是否隐藏
@property (nonatomic, strong) NSIndexPath *indexPath; //索引路径
#TODO:
// 视图类型的常量
@property (nonatomic, readonly) UICollectionElementCategory representedElementCategory;
// 视图布局特定标识符
@property (nonatomic, readonly, nullable) NSString *representedElementKind; // nil when representedElementCategory is UICollectionElementCategoryCell
# TODO:方法
//创建并返回一个Cell的布局属性对象
+ (instancetype)layoutAttributesForCellWithIndexPath:(NSIndexPath *)indexPath;
// 创建并返回追加视图的布局属性对象
+ (instancetype)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind withIndexPath:(NSIndexPath *)indexPath;
// 创建并返回装饰视图的布局属性对象
+ (instancetype)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind withIndexPath:(NSIndexPath *)indexPath;
@end
3. UICollectionViewUpdateItem
描述在集合视图中对item进行单个修改的对象
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionViewUpdateItem : NSObject
// 更新前项目的索引路径
@property (nonatomic, readonly, nullable) NSIndexPath *indexPathBeforeUpdate; // nil for UICollectionUpdateActionInsert
// 更新后的项目的索引路径。
@property (nonatomic, readonly, nullable) NSIndexPath *indexPathAfterUpdate; // nil for UICollectionUpdateActionDelete
// 对该item执行的操作。
@property (nonatomic, readonly) UICollectionUpdateAction updateAction;
@end
4. UICollectionViewLayoutInvalidationContext
当布局无效时,声明布局的哪些部分需要更新的上下文对象。
NS_CLASS_AVAILABLE_IOS(7_0) @interface UICollectionViewLayoutInvalidationContext : NSObject
#TODO:
// 表示所有布局数据都应标记为无效
@property (nonatomic, readonly) BOOL invalidateEverything; // set to YES when invalidation occurs because the collection view is sent -reloadData
//指示布局是否要求新的部分和item计数
@property (nonatomic, readonly) BOOL invalidateDataSourceCounts; // if YES, the layout should requery section and item counts from the collection view - set to YES when the collection view is sent -reloadData and when items are inserted or deleted
// 将指定索引路径上的单元格添加到无效项列表中。
- (void)invalidateItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(8_0);
// 将指定索引路径的补充视图添加到无效项列表中。
- (void)invalidateSupplementaryElementsOfKind:(NSString *)elementKind atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(8_0);
// 将指定索引路径上的装饰视图添加到无效项列表中。
- (void)invalidateDecorationElementsOfKind:(NSString *)elementKind atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(8_0);
//一组索引路径,表示无效的单元格。
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *invalidatedItemIndexPaths NS_AVAILABLE_IOS(8_0);
//一个字典,用于识别无效的补充视图。
@property (nonatomic, readonly, nullable) NSDictionary<NSString *, NSArray<NSIndexPath *> *> *invalidatedSupplementaryIndexPaths NS_AVAILABLE_IOS(8_0); // keys are element kind strings - values are NSArrays of NSIndexPaths
// 用于识别无效的装饰视图的字典。
@property (nonatomic, readonly, nullable) NSDictionary<NSString *, NSArray<NSIndexPath *> *> *invalidatedDecorationIndexPaths NS_AVAILABLE_IOS(8_0); // keys are element kind strings - values are NSArrays of NSIndexPaths
// 要应用于集合视图的内容偏移量的增量值
@property (nonatomic) CGPoint contentOffsetAdjustment NS_AVAILABLE_IOS(8_0); // delta to be applied to the collection view's current contentOffset - default is CGPointZero
// 要应用于集合视图的内容大小的增量值。
@property (nonatomic) CGSize contentSizeAdjustment NS_AVAILABLE_IOS(8_0); // delta to be applied to the current content size - default is CGSizeZero
#TODO:
// Reordering support
// 一组索引路径,表示集合视图中移动项目的先前位置。
@property (nonatomic, readonly, copy, nullable) NSArray<NSIndexPath *> *previousIndexPathsForInteractivelyMovingItems NS_AVAILABLE_IOS(9_0); // index paths of moving items prior to the invalidation
// 索引路径数组,表示集合视图中移动项目的新位置。
@property (nonatomic, readonly, copy, nullable) NSArray<NSIndexPath *> *targetIndexPathsForInteractivelyMovingItems NS_AVAILABLE_IOS(9_0); // index paths of moved items following the invalidation
// 当前点用于确定移动项目的位置。
@property (nonatomic, readonly) CGPoint interactiveMovementTarget NS_AVAILABLE_IOS(9_0);
@end
5. UICollectionElementCategory 视图类型常量
typedef NS_ENUM(NSUInteger, UICollectionElementCategory) {
UICollectionElementCategoryCell,
UICollectionElementCategorySupplementaryView,
UICollectionElementCategoryDecorationView
};
基于UICollectionViewLayoutInvalidationContext 类型的属性。如果你自定义了UICollectionViewLayoutInvalidationContext ,就应该重载该方法,并使用自定义的类型。重载该方法的时候,必须调用super类。
传送门:
官方文档
不错的参数博客