UICollectionView.h


#if USE_UIKIT_PUBLIC_HEADERS || !__has_include(<UIKitCore/UICollectionView.h>)
//
//  UICollectionView.h
//  UIKit
//
//  Copyright (c) 2011-2018 Apple Inc. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/NSIndexPath+UIKitAdditions.h>
#import <UIKit/UIScrollView.h>
#import <UIKit/UIKitDefines.h>
#import <UIKit/UIDataSourceTranslating.h>
#import <UIKit/UIDropInteraction.h>
#import <UIKit/UISpringLoadedInteractionSupporting.h>

NS_ASSUME_NONNULL_BEGIN



/* 滚动位置 <位移枚举> */
// 三个 垂直或水平 位置相互互斥,但是与水平滚动位置成比例或不同,组合来自相同分组(水平/垂直)的位置将导致NSInvalidArgumentException
typedef NS_OPTIONS(NSUInteger, UICollectionViewScrollPosition) {
    UICollectionViewScrollPositionNone                 = 0,         /// 无
    
    UICollectionViewScrollPositionTop                  = 1 << 0,    /// 上
    UICollectionViewScrollPositionCenteredVertically   = 1 << 1,    /// 垂直居中
    UICollectionViewScrollPositionBottom               = 1 << 2,    /// 下
    
    UICollectionViewScrollPositionLeft                 = 1 << 3,    /// 左
    UICollectionViewScrollPositionCenteredHorizontally = 1 << 4,    /// 水平居中
    UICollectionViewScrollPositionRight                = 1 << 5     /// 右
};

/* 根据速率排序 <枚举> */
typedef NS_ENUM(NSInteger, UICollectionViewReorderingCadence) {
    UICollectionViewReorderingCadenceImmediate, /// 即时
    UICollectionViewReorderingCadenceFast,      /// 快速
    UICollectionViewReorderingCadenceSlow       /// 慢
} API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);



@class UICollectionView, UICollectionReusableView, UICollectionViewCell, UICollectionViewLayout, UICollectionViewTransitionLayout, UICollectionViewLayoutAttributes, UITouch, UINib;
@class UIDragItem, UIDragPreviewParameters, UIDragPreviewTarget;
@class UICollectionViewDropProposal, UICollectionViewPlaceholder, UICollectionViewDropPlaceholder;
@protocol UIDataSourceTranslating, UISpringLoadedInteractionContext;
@protocol UIDragSession, UIDropSession;
@protocol UICollectionViewDragDelegate, UICollectionViewDropDelegate, UICollectionViewDropCoordinator, UICollectionViewDropItem, UICollectionViewDropPlaceholderContext;

// layout transition block signature
typedef void (^UICollectionViewLayoutInteractiveTransitionCompletion)(BOOL completed, BOOL finished);





#pragma mark - 集合视图重点更新背景Class
#pragma mark -
/*
 - 概述
    焦点更改时,集合视图委托接收具有相关信息的上下文对象。 您的委托方法使用此对象中的信息来创建动画或执行与焦点更改相关的其他任务。
 */
NS_CLASS_AVAILABLE_IOS(9_0) @interface UICollectionViewFocusUpdateContext : UIFocusUpdateContext

/// 之前具有焦点的集合视图单元格的索引路径
@property (nonatomic, strong, readonly, nullable) NSIndexPath *previouslyFocusedIndexPath;
/// 正在接收焦点的集合视图单元的索引路径
@property (nonatomic, strong, readonly, nullable) NSIndexPath *nextFocusedIndexPath;

@end





///!!!: 数据源 <协议>
@protocol UICollectionViewDataSource <NSObject>
@required
// 每组返回多少个Item
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
// 设置每一个返回的Cell
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
@optional
// 返回多少组
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
/*
 设置组头/组尾
 - 用法:
 - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    // 创建头部或尾部视图
    if (kind == UICollectionElementKindSectionHeader) {
    UICollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:indexPath];
    return headerView;
    // 返回头部视图
    } else {  // UICollectionElementKindSectionFooter 返回尾部视图
    UICollectionReusableView *footerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"footer" forIndexPath:indexPath];
    return footerView;
    }
 }
 */
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
// 是否允许移动某个Item
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
// 移动Item之后的回调(sourceIndexPath:原始移动的索引;destinationIndexPath:移动到目的位置的索引)
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0);
// 返回右侧索引视图的格式(如[“A”,“B”,“C”...“Z”,“#”])
- (nullable NSArray<NSString *> *)indexTitlesForCollectionView:(UICollectionView *)collectionView API_AVAILABLE(tvos(10.2));
// 返回指定索引对应的索引路径
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView indexPathForIndexTitle:(NSString *)title atIndex:(NSInteger)index API_AVAILABLE(tvos(10.2));
@end





///!!!: 预加载数据源 <协议>
// 这两个代理方法并不能代替之前的数据源方法,仅仅是辅助加载数据,允许触发异步数据加载操作
@protocol UICollectionViewDataSourcePrefetching <NSObject>
@required
// 预加载对应索引上的单元格
- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);
@optional
// 取消先前触发的数据预读请求
- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths  NS_AVAILABLE_IOS(10_0);
@end





///!!!: 代理 <协议>
// 允许您管理CollectionView中Item的选择或高亮,并对这些Item执行操作
@protocol UICollectionViewDelegate <UIScrollViewDelegate>
@optional
// 是否高亮
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
// 已经高亮
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
// 已经结束高亮
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;

// 是否选中Item
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
// 是否取消选中Item(多选模式下点击已选择的Item时调用)
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
// 已经选中Item
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
// 已经取消选中Item
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;

// 将要加载 Item(使用此方法监测添加的Item,而不是关注Item何时出现)
- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
// 将要加载 可重用的View(如:注册的组头/组尾View)
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(8_0);
// 已经删除 Item
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath;
// 已经删除 可重用的View
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;

/* 这三个方法应该同时使用 */
// 是否显示长按菜单
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath;
// 设置显示的菜单选项
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
// 点击菜单上的选项后触发的方法(测试只有copy,cut,paste方法可用)
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;

// CollectionView重新布局时的回调
- (nonnull UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout;

/* 聚焦(用于Apple TV) */
// 是否聚焦指定索引上的控件
- (BOOL)collectionView:(UICollectionView *)collectionView canFocusItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
// 是否改变聚焦
- (BOOL)collectionView:(UICollectionView *)collectionView shouldUpdateFocusInContext:(UICollectionViewFocusUpdateContext *)context NS_AVAILABLE_IOS(9_0);
// 聚焦已经更新
- (void)collectionView:(UICollectionView *)collectionView didUpdateFocusInContext:(UICollectionViewFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator NS_AVAILABLE_IOS(9_0);
// 询问应该关注的单元格的索引路径
- (nullable NSIndexPath *)indexPathForPreferredFocusedViewInCollectionView:(UICollectionView *)collectionView NS_AVAILABLE_IOS(9_0);
// 移动项目时使用的索引路径
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath NS_AVAILABLE_IOS(9_0);
// 布局更改和动画更新时自定义内容偏移
- (CGPoint)collectionView:(UICollectionView *)collectionView targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset NS_AVAILABLE_IOS(9_0);

// 是否要为指定项目显示弹簧加载效果(如果要在弹簧加载的单元格的不同子视图上进行交互效果,请修改context.targetView属性;默认:YES)
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSpringLoadItemAtIndexPath:(NSIndexPath *)indexPath withContext:(id<UISpringLoadedInteractionContext>)context API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);
@end





#pragma mark - 集合视图Class
#pragma mark -
/*
 - iOS6 推出,和UITableView的API设计类似,并做了一定扩展
 */
NS_CLASS_AVAILABLE_IOS(6_0) @interface UICollectionView : UIScrollView <UIDataSourceTranslating>
/* 实例化方法 */
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;

/// 设置 布局
@property (nonatomic, strong) UICollectionViewLayout *collectionViewLayout;
/// 设置 代理
@property (nonatomic, weak, nullable) id <UICollectionViewDelegate> delegate;
/// 设置 数据源
@property (nonatomic, weak, nullable) id <UICollectionViewDataSource> dataSource;

/// 设置 预读数据源(当前滑动到某个区域后,根据这次滑动的方向接下来可能滑向哪些indexPaths,可以提前做数据预备或销毁;UICollectionView和UITableView都支持)
@property (nonatomic, weak, nullable) id<UICollectionViewDataSourcePrefetching> prefetchDataSource NS_AVAILABLE_IOS(10_0);
/// 是否开启 数据预读
@property (nonatomic, getter=isPrefetchingEnabled) BOOL prefetchingEnabled NS_AVAILABLE_IOS(10_0);

/// 设置 拖拽代理
@property (nonatomic, weak, nullable) id <UICollectionViewDragDelegate> dragDelegate API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);
/// 设置 下拉代理
@property (nonatomic, weak, nullable) id <UICollectionViewDropDelegate> dropDelegate API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

/// 是否开启 拖动(默认:iPad_YES,iPhone_NO)
@property (nonatomic) BOOL dragInteractionEnabled API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);
/// 设置 拖动时排序速率(默认:UICollectionViewReorderingCadenceImmediate)
@property (nonatomic) UICollectionViewReorderingCadence reorderingCadence API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);

/// 设置 背景View(会自动填充整个UICollectionView;在所有Item和其他补充View的下层)
@property (nonatomic, strong, nullable) UIView *backgroundView;

/* 注册 */
// 注册 Item(代码方式)
- (void)registerClass:(nullable Class)cellClass forCellWithReuseIdentifier:(NSString *)identifier;
// 注册 Item(Xib方式)
- (void)registerNib:(nullable UINib *)nib forCellWithReuseIdentifier:(NSString *)identifier;
// 注册 组头/组尾(代码方式)
// - elementKind:UICollectionElementKindSectionHeader/UICollectionElementKindSectionFooter
- (void)registerClass:(nullable Class)viewClass forSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier;
// 注册 组头/组尾(Xib方式)
- (void)registerNib:(nullable UINib *)nib forSupplementaryViewOfKind:(NSString *)kind withReuseIdentifier:(NSString *)identifier;

/* 缓存池获取 */
// 获取 对应标识符的Item
- (__kindof UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;
// 获取 对应元素种类的组头/组尾
- (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewOfKind:(NSString *)elementKind withReuseIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath;

/// 是否允许 选中Item(默认:YES)
@property (nonatomic) BOOL allowsSelection;
/// 是否允许 多选Item(默认:NO)
@property (nonatomic) BOOL allowsMultipleSelection;

/* 获取 一组选中的Item索引(只读;默认:nil) */
#if UIKIT_DEFINE_AS_PROPERTIES
@property (nonatomic, readonly, nullable) NSArray<NSIndexPath *> *indexPathsForSelectedItems;
#else
- (nullable NSArray<NSIndexPath *> *)indexPathsForSelectedItems;
#endif

// 设置 选中某个Item的动画
- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition;
// 设置 取消选中某个Item的动画
- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated;

// 获取 当前是否有未提交的更新
@property (nonatomic, readonly) BOOL hasUncommittedUpdates API_AVAILABLE(ios(11.0));

// 刷新数据(会丢弃未提交的更新,如:placeholders)
- (void)reloadData;

/* 设置布局 */
// 从一个布局过度到另一个布局(带动画)
- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout animated:(BOOL)animated;
// 从一个布局过度到另一个布局(带完成后的回调)
- (void)setCollectionViewLayout:(UICollectionViewLayout *)layout animated:(BOOL)animated completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
// 可对布局更改后的动画进行设置,这个方法传入一个布局策略layout,系统会开始进行布局渲染,返回一个UICollectionViewTransitionLayout对象,这个UICollectionViewTransitionLayout对象管理动画的相关属性,我们可以进行设置
- (UICollectionViewTransitionLayout *)startInteractiveTransitionToCollectionViewLayout:(UICollectionViewLayout *)layout completion:(nullable UICollectionViewLayoutInteractiveTransitionCompletion)completion NS_AVAILABLE_IOS(7_0);
// 准备好动画设置后,我们需要调用下面的方法进行布局动画的展示,之后会调用上面方法的block回调
- (void)finishInteractiveTransition NS_AVAILABLE_IOS(7_0);
// 取消布局动画设置,之后也会进行上面方法的block回调
- (void)cancelInteractiveTransition NS_AVAILABLE_IOS(7_0);


/* 获取 有多少组 */
#if UIKIT_DEFINE_AS_PROPERTIES
@property (nonatomic, readonly) NSInteger numberOfSections;
#else
- (NSInteger)numberOfSections;
#endif
// 获取 某一组有多少个Item
- (NSInteger)numberOfItemsInSection:(NSInteger)section;

// 获取 某个Item的Layout信息
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath;
// 获取 某个组头/组尾的Layout信息
- (nullable UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;

// 获取 某个点所在的IndexPath信息
- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point;
// 获取 某个Item所在的IndexPath信息
- (nullable NSIndexPath *)indexPathForCell:(UICollectionViewCell *)cell;
// 获取 某个IndexPath对应的Item
- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath;

/* 获取 当前可见的Item 和 IndexPath信息 */
#if UIKIT_DEFINE_AS_PROPERTIES
@property (nonatomic, readonly) NSArray<__kindof UICollectionViewCell *> *visibleCells;
@property (nonatomic, readonly) NSArray<NSIndexPath *> *indexPathsForVisibleItems;
#else
- (NSArray<__kindof UICollectionViewCell *> *)visibleCells;
- (NSArray<NSIndexPath *> *)indexPathsForVisibleItems;
#endif

// 获取 组头/组尾View
- (nullable UICollectionReusableView *)supplementaryViewForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
// 获取 一组组头/组尾View
- (NSArray<UICollectionReusableView *> *)visibleSupplementaryViewsOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(9_0);
// 获取 一组组头/组尾View所在的IndexPath信息
- (NSArray<NSIndexPath *> *)indexPathsForVisibleSupplementaryElementsOfKind:(NSString *)elementKind NS_AVAILABLE_IOS(9_0);

/* CollectionView交互相关 */
// 滚动到 指定IndexPath
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated;

// 插入一组Item
- (void)insertSections:(NSIndexSet *)sections;
// 删除一组Item
- (void)deleteSections:(NSIndexSet *)sections;
// 重新加载一组Item
- (void)reloadSections:(NSIndexSet *)sections;
// 移动一组Item到另一组
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;

// 指定IndexPath插入Item
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
// 删除指定IndexPath的Item
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
// 重新加载指定IndexPath的Item
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
// 移动Item从一个IndexPath到另一个IndexPath
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;

// 多个操作(插入/删除/重新加载/移动)时进行动画处理
- (void)performBatchUpdates:(void (NS_NOESCAPE ^ _Nullable)(void))updates completion:(void (^ _Nullable)(BOOL finished))completion;

/* 交互式移动(Item根据手势移动) */
// 是否允许 某个IndexPath上的Item交互式移动(默认:NO)
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
// 根据手势移动 实时更新Item位置
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition NS_AVAILABLE_IOS(9_0);
// 结束交互式移动跟踪并将Item移动到新位置
- (void)endInteractiveMovement NS_AVAILABLE_IOS(9_0);
// 结束交互式移动跟踪并将Item放回原始位置
- (void)cancelInteractiveMovement NS_AVAILABLE_IOS(9_0);

// 是否开启 自动选中Item(自动选择最后选中的Item,如果从未选择就默认第一个Item;默认:NO)
@property (nonatomic) BOOL remembersLastFocusedIndexPath NS_AVAILABLE_IOS(9_0);

// 获取 是否正在拖某个Item(拖动会话当前处于活动状态则为YES)
@property (nonatomic, readonly) BOOL hasActiveDrag API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);
// 获取 是否已经放下
@property (nonatomic, readonly) BOOL hasActiveDrop API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);
@end





#pragma mark - 拖拽放置 <分类>
#if TARGET_OS_IOS
@interface UICollectionView (UIDragAndDrop) <UISpringLoadedInteractionSupporting>
@end
#endif



///!!!: 拖拽代理 <协议>
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@protocol UICollectionViewDragDelegate <NSObject>
@required
// 开始拖动Item(如果返回nil,则此Item无法拖动)
- (NSArray<UIDragItem *> *)collectionView:(UICollectionView *)collectionView itemsForBeginningDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath;

@optional
// 一次拖动多个Item
- (NSArray<UIDragItem *> *)collectionView:(UICollectionView *)collectionView itemsForAddingToDragSession:(id<UIDragSession>)session atIndexPath:(NSIndexPath *)indexPath point:(CGPoint)point;
// 自定义拖动过程中Item外观(如果没有实现该方法,或实现后返回nil,将使用Item原样式)
- (nullable UIDragPreviewParameters *)collectionView:(UICollectionView *)collectionView dragPreviewParametersForItemAtIndexPath:(NSIndexPath *)indexPath;
// 将要开始拖动(在拖动动画完成后调用)
- (void)collectionView:(UICollectionView *)collectionView dragSessionWillBegin:(id<UIDragSession>)session;
// 已经结束拖动(在拖动结束后调用,通常是因为内容被删除或拖动被中止;使用此方法可以关闭与应用程序中拖动会话管理相关的任何任务)
- (void)collectionView:(UICollectionView *)collectionView dragSessionDidEnd:(id<UIDragSession>)session;

// 是否允许 拖动(请参阅UICollectionViewDropProposal.operation;默认:YES)
- (BOOL)collectionView:(UICollectionView *)collectionView dragSessionAllowsMoveOperation:(id<UIDragSession>)session;
// 是否不允许 拖动会话进入另一个应用程序(默认:NO)
- (BOOL)collectionView:(UICollectionView *)collectionView dragSessionIsRestrictedToDraggingApplication:(id<UIDragSession>)session;
@end



///!!!: 放置代理 <协议>
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@protocol UICollectionViewDropDelegate <NSObject>
@required
// 开始放置时的回调(用户拖动Item之后收支离开屏幕时)
- (void)collectionView:(UICollectionView *)collectionView performDropWithCoordinator:(id<UICollectionViewDropCoordinator>)coordinator;

@optional
// Item被拖到CollectionView边界时的回调(查看当前数据模型是否可以继续接收拖动的内容,如果YES,会继续调用其它代理方法;默认:YES)
- (BOOL)collectionView:(UICollectionView *)collectionView canHandleDropSession:(id<UIDropSession>)session;
// 已经放置时的回调
- (void)collectionView:(UICollectionView *)collectionView dropSessionDidEnter:(id<UIDropSession>)session;
// 跟踪放置会话时的回调(该方法会实时反馈如何合并,放置拖动的Item到当前视图)
- (UICollectionViewDropProposal *)collectionView:(UICollectionView *)collectionView dropSessionDidUpdate:(id<UIDropSession>)session withDestinationIndexPath:(nullable NSIndexPath *)destinationIndexPath;
// 放置会话已经结束时的回调(拖动超出CollectionView时调用)
- (void)collectionView:(UICollectionView *)collectionView dropSessionDidExit:(id<UIDropSession>)session;
// 放置会话已经结束时的回调(最终结束时的回调)
- (void)collectionView:(UICollectionView *)collectionView dropSessionDidEnd:(id<UIDropSession>)session;
// 放置会话进行时的预览(可通过此方法自定义预览效果;通过 - [UICollectionViewDropCoordinator dropItem:toItemAtIndexPath:]设置动画时,将根据需要调用此方法)
- (nullable UIDragPreviewParameters *)collectionView:(UICollectionView *)collectionView dropPreviewParametersForItemAtIndexPath:(NSIndexPath *)indexPath;
@end





/* 放置方案 <枚举> */
typedef NS_ENUM(NSInteger, UICollectionViewDropIntent) {
    UICollectionViewDropIntentUnspecified,                      /// 没有指定放置方案(不会有间隙,您可能希望提供一些视觉处理以将此信息传达给用户)
    UICollectionViewDropIntentInsertAtDestinationIndexPath,     /// 丢弃放置,将删除的Item插入指定的索引路径(在指定位置打开一个间隙,模拟最终掉落的布局)
    UICollectionViewDropIntentInsertIntoDestinationIndexPath,   /// 丢弃放置,将删除的Item合并到指定索引路径的项目中(不会有间隙,集合视图将突出显示目标索引路径上的项目)
    
} API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos);



#pragma mark - 放置方案Class
#pragma mark -
UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@interface UICollectionViewDropProposal : UIDropProposal
// 实例化放置方案Class
- (instancetype)initWithDropOperation:(UIDropOperation)operation intent:(UICollectionViewDropIntent)intent;
/// 设置 放置方案(如何处理已经拖动放置后的Item;默认:UICollectionViewDropIntentUnspecified)
@property (nonatomic, readonly) UICollectionViewDropIntent intent;
@end





///!!!: 自定义放置 <协议>
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@protocol UICollectionViewDropCoordinator <NSObject>
/// 获取 正在拖动的Item
@property (nonatomic, readonly) NSArray<id<UICollectionViewDropItem>> *items;
/// 获取 最后一次下拉的IndexPath
@property (nonatomic, readonly, nullable) NSIndexPath *destinationIndexPath;
/// 获取 如何合并放置的Item的当前提案
@property (nonatomic, readonly) UICollectionViewDropProposal *proposal;
/// 获取 Drop包含的事务信息
@property (nonatomic, readonly) id<UIDropSession> session;

// 自定义Item放置位置(放置到指定Placeholder)
/*
 将为重用标识符创建占位符单元格,并将其插入指定的indexPath,而无需进行dataSource更新
 只要占位符单元格可见,就会调用cellUpdateHandler;;-collectionView:cellForItemAtIndexPath:不会为占位符调用
 一旦dragItem数据可用,您就可以使用最终单元格交换临时占位符单元格
 */
- (id<UICollectionViewDropPlaceholderContext>)dropItem:(UIDragItem *)dragItem toPlaceholder:(UICollectionViewDropPlaceholder*)placeholder;
// 自定义Item放置位置(放置到指定索引;必须调用-performBatchUpdates:completion:更新数据源并在调用此方法之前将新项插入到集合视图中)
- (id<UIDragAnimating>)dropItem:(UIDragItem *)dragItem toItemAtIndexPath:(NSIndexPath *)indexPath;
// 自定义Item放置位置(放置到指定索引和范围;Rect为0时,缩小到单个点)
- (id<UIDragAnimating>)dropItem:(UIDragItem *)dragItem intoItemAtIndexPath:(NSIndexPath *)indexPath rect:(CGRect)rect;
// 自定义Item放置位置(放置到手指触摸点)
- (id<UIDragAnimating>)dropItem:(UIDragItem *)dragItem toTarget:(UIDragPreviewTarget *)target;
@end





#pragma mark - 占位符Class
#pragma mark -
UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos,watchos)
@interface UICollectionViewPlaceholder : NSObject
/* 实例化方法 */
// 实例化(设置插入IndexPath和重用标识符)
- (instancetype)initWithInsertionIndexPath:(NSIndexPath*)insertionIndexPath reuseIdentifier:(NSString *)reuseIdentifier NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/// 当placeholder Item可见时调用以更新Item
@property (nonatomic, nullable, copy) void(^cellUpdateHandler)(__kindof UICollectionViewCell *);
@end





#pragma mark - 放置占位符Class
#pragma mark -
UIKIT_EXTERN API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos,watchos)
@interface UICollectionViewDropPlaceholder : UICollectionViewPlaceholder
/// 修改动画放置项的外观(如:visiblePath以提供占位符单元的"剪切"区域)
@property (nonatomic, nullable, copy) UIDragPreviewParameters * _Nullable (^previewParametersProvider)(__kindof UICollectionViewCell *);
@end





///!!!: 放置项 <协议>
/*
 - 当Drop时,可以从UICollectionViewDropCoordinator的items属性中获取此类,不需要手动实例化此类
 - 此类提供 正在拖动的Item的相关数据
 */
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@protocol UICollectionViewDropItem <NSObject>
/// 获取 拖动的项目
@property (nonatomic, readonly) UIDragItem *dragItem;
/// 获取 拖动项目的索引路径(如果拖拽项来自CollectionView,则此索引指示Item的位置;如果放置项是其他来源,则为nil;这对于直接访问数据源中的模型对象而不是使用项提供程序来检索数据非常有用)
@property (nonatomic, readonly, nullable) NSIndexPath *sourceIndexPath;
/// 获取 拖动项目的预览尺寸(如果dragItem没有可见的拖放预览,则返回CGSizeZero;对于- [UICollectionViewDropCoordinator dropItem:toTarget:] 方法,计算UIDragPreviewTarget变化可能很有用)
@property (nonatomic, readonly) CGSize previewSize;
@end





///!!!: 放置占位符信息 <协议>
API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(tvos, watchos)
@protocol UICollectionViewDropPlaceholderContext <UIDragAnimating>
/// 获取 dragItem的itemProvider中检索drop数据
@property (nonatomic, readonly) UIDragItem *dragItem;

// 交换最终Item的占位符
/*
 - 您只负责使用提供的insertionIndexPath更新块内的数据源
 - 如果占位符不再可用(如:已调用-reloadData),则不会执行dataSourceUpdates回调,并返回NO
 */
- (BOOL)commitInsertionWithDataSourceUpdates:(void(NS_NOESCAPE ^)(NSIndexPath *insertionIndexPath))dataSourceUpdates;
// 完全删除不需要的占位符Item(如果不再需要占位符,或者您希望手动为放置数据插入单元格,则可以通过此方法删除占位符)
- (BOOL)deletePlaceholder;
// 更新占位符单元格的内容
/*
 - 调用此方法以通过updateCellHandler请求更新占位符单元格的内容
 - 仅在占位符可见时才会调用此选项,如果通过滚动显示占位符,将自动调用updateCellHandler
 */
- (void)setNeedsCellUpdate;
@end





NS_ASSUME_NONNULL_END

#else
#import <UIKitCore/UICollectionView.h>
#endif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,045评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,114评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,120评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,902评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,828评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,132评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,590评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,258评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,408评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,335评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,385评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,068评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,660评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,747评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,967评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,406评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,970评论 2 341

推荐阅读更多精彩内容