测试已测试 | ✓ |
Lang语言 | Obj-CObjective C |
许可 | MIT |
Released上次发布 | 2014年12月 |
由 未声明者 维护.
SSCollectionViewExchangeController
管理在集合视图中交换 2 个项目的过程。其关键词是 交换,与更常见的移动场景相比,结果略有不同。考虑以下 5 个项目的示例。
item1 item2 item3 item4 item5
在移动场景中,当项目 1 移动到项目 5 时,这是结果:
item2 item3 item4 item5 item1
在移动过程中,从 from 项到 to 项之间的所有项目都重新定位到 from 项的原始位置。
在交换场景中,当项目 1 与项目 5 交换时,这是结果:
item5 item2 item3 item4 item1
在交换中,to 项和 from 项之间的项目不会移动。只有 from 项和 to 项会移动。
---------------------
| | delegate
---| ViewController |<----------------------
| | | |
| --------------------- |
| | ------------------------------------------
| | @property (strong, ... | |
| -------------------------->| SSCollectionViewExchangeController |
| | |
| ------------------------------------------
| | |
| V |
| ---------------------------------- |
| | | |
| ------->| UILongPressGestureRecognizer | |
| | | | |
| | ---------------------------------- |
| ------------------------ V
| | | ---------------------------------------
-->| UICollectionView |----------> | |
| | | SSCollectionViewExchangeLayout |
------------------------ | |
---------------------------------------
初始化 SSCollectionViewExchangeController
的实例时,它会为您集合视图安装一个手势识别器,并创建一个自定义布局对象,即 SSCollectionViewExchangeLayout
,它是一个 UICollectionViewFlowLayout
的子类,并设置在您的集合视图上。布局管理交换过程中集合视图中的项目显示。通过 SSCollectionViewExchangeControllerDelegate
协议,您的视图控制器始终被通知,允许您在集合视图上发生更改时保持您的模型同步,并在过程中执行任何所需的实时更新。您的视图控制器必须保留对交换控制器的强引用。
如果您的视图包含多个集合视图,您可以拥有每个集合视图的交换控制器。但不能在集合视图之间进行交换。
捕获:捕获是交换过程的开始。用户通过在集合视图单元格上长按来启动此过程。如果手势被识别,捕获动画运行(默认或您的自定义动画)来向用户表明捕获成功。
释放:用户放手指,通常是另一个项目上方,结束过程。释放动画运行(再次,默认或您的自定义动画)。
交换交易:交换交易从抓取开始,到释放结束,通常是在另一个项目上进行,导致两个项目交换。然而,在抓取和释放之间,用户可能拖动多个项目,包括,可能的话,起始位置的项目。
交换事件:在交换交易期间,每次用户将拖动到不同的项目时都会发生交换事件。单个交换交易中可以包含多个交换事件。交换事件包含一个或两个单个交换。如果是交易中的第一个交换事件,则在被拖动的项目和被拖动的目标项目之间有一个单个交换。如果用户继续拖动到新项目,后续的交换事件将包含两个单个交换:一个是撤销上一个交换,另一个是新的交换。请参阅下面的时间线图。
位移项:当用户拖动到新项目时,该项目会被位移。它动画地回到被拖动的项目的原始位置。同时,之前被位移的项目会动画地回到其原始位置。为了指示位移项,布局会降低其不透明度。
隐藏项:在抓取和释放之间,被拖动项目的单元格被隐藏。这是由布局管理的。尽管如此,隐藏项在交换交易进行时仍跟随用户。如果您好奇,请从 indexPathForItemToHide
返回 nil,您将能够观察这一点,最好在带有慢速动画的模拟器中查看。
快照:在抓取期间,单元格的快照被创建。快照在长按期间跟随用户的指尖。
抓取矩形:在某些实现中,集合视图的单元格只有在长按发生在单元格内的特定矩形上时才能被抓取。这就是抓取矩形。请参阅可选的 exchangeController:viewForCatchRectangleForItemAtIndexPath:
代理方法。
Exchange Transaction
|--------------------------------------------------------------------------------------------->|
Catch at index path 0,3 Release at index path 1,5
Exchange Event 1 Exchange Event 2 Exchange Event 3
|----------------------------->||----------------------------->||----------------------------->|
move from: 0,3 to 1,3 1,3 to 1,4 1,4 to 1,5
new exchange undo previous new exchange undo previous new exchange
|----------------------------->||------------->||------------>||-------------->||------------->|
exchange: 0,3 with 1,3 1,3 with 0,3 0,3 with 1,4 1,4 with 0,3 0,3 and 1,5
在时间线中,您可以看到一个交换交易可以包括多个交换事件。交换事件有一个或两个单个交换。如果有两个,第一个是用来撤销之前的交换。如果只有一个,则没有之前的交换可以撤销。
您可以在 这里 查看演示应用程序 SSCollectionViewExchangeController
。
SSCollectionViewExchangeController
使用 UICollectionView
,从 iOS 6.0 开始可用。
或者,将这些 8 个文件复制到您的 Xcode 项目中
在您的视图控制器中导入 SSCollectionViewExchangeController.h
...
#import "SSCollectionViewExchangeController.h"
采用 SSCollectionViewExchangeControllerDelegate
协议...
@interface MyViewController () <SSCollectionViewExchangeControllerDelegate>
为交换控制器创建一个属性...
@property (strong, nonatomic) SSCollectionViewExchangeController *exchangeController;
在 viewDidLoad
中创建 SSCollectionViewExchangeController
的实例...
self.exchangeController = [[SSCollectionViewExchangeController alloc] initWithDelegate:self
collectionView:self.collectionView];
获取布局并按需配置...
UICollectionViewFlowLayout *layout = self.exchangeController.layout;
layout.itemSize = CGSizeMake(150, 30);
...
实现下一节中描述的所需协议方法。
可选。本示例应用包含一个关于 NSMutableArray
的类别,它实现了交换两个可能位于不同数组中的项目的功能。您可以导入该类别,并在您的 exchangeController:didExchangeItemAtIndexPath1:withItemAtIndexPath2:
代理方法实现中使用该方法。请参考演示应用中的 ViewController.m
文件以获取示例实现。
[NSMutableArray exchangeObjectInArray:array atIndex:indexPath1.item
withObjectInOtherArray:otherArray atIndex:indexPath2.item];
注意:如果您的集合视图有多个部分,且每个部分都有一个数组,请参考 ViewController.m
中的 arrayForSection:
方法以了解如何将集合视图的部分映射到数组。您可能需要实现类似的内容。
可选。交换控制器在交换过程中提供了默认动画来向用户提供反馈。一些与这些动画相关的属性被暴露出来,以便您可以根据需要配置它们。请参考以下属性声明中的注释。
可选。如果您认为公开的属性未能提供足够的控制,则可以实现以下可选代理方法...
- (void) exchangeController:(SSCollectionViewExchangeController *)exchangeController
didExchangeItemAtIndexPath1:(NSIndexPath *)indexPath1
withItemAtIndexPath2:(NSIndexPath *)indexPath2;
在交换事件中调用每个单个交换。每个事件可能有一个或两个交换。无论哪种情况,代理都应该只通过在指示的索引路径处交换元素来更新模型。请参考上述交换事件描述和“交换事务和交换事件:时间线视图”。此方法为代理提供了与视图上发生的变化同步其模型的机会。如果您正在执行任何类型的实时更新(如用户拖动),则通常不在此调用此方法,因为这个方法对于每个交换事件可能会被调用两次。实时更新应在 exchangeControllerDidFinishExchangeEvent:
中实现。
- (void)exchangeControllerDidFinishExchangeEvent:(SSCollectionViewExchangeController *)exchangeController;
当交换事务完成(用户抬起手指)时调用。此方法为代理提供了用户拖动时执行实时更新的机会。
- (void)exchangeControllerDidFinishExchangeTransaction:(SSCollectionViewExchangeController *)exchangeController
withIndexPath1:(NSIndexPath *)indexPath1
indexPath2:(NSIndexPath *)indexPath2;
当交换事务完成时调用(用户抬起手指)。索引路径代表最终交换的两个项目。不要交换这些项目,因为您已经交换了。此方法允许代理在事务结束时执行所需的任何任务,例如设置撤销。如果用户将手指拖回起始位置并释放(实际上没有交换),则索引路径将相同。
- (void)exchangeControllerDidCancelExchangeTransaction:(SSCollectionViewExchangeController *)exchangeController;
当长按手势识别器的状态变为 UIGestureRecognizerStateCancelled
时调用。通常,这仅在交换事务期间设备接收到电话时发生。当发生这种情况时,交换控制器会帮助代理返回到交换事务开始之前的状态。为此,交换控制器首先在代理上调用 exchangeController:didExchangeItemAtIndexPath1:withItemAtIndexPath2:
并传递最后交换项目的索引路径,以便代理可以恢复模型。然后调用此方法,使代理执行所需的任何其他操作。通常,代理不需要在对集合视图采取任何行动。交换控制器将集合视图返回到其以前的状态。因为没有应用动画,所以视图是隐藏的。
- (BOOL)exchangeControllerCanBeginExchangeTransaction:(SSCollectionViewExchangeController *)exchangeController
withItemAtIndexPath:(NSIndexPath *)indexPath;
如果实现,则在开始交换事务之前调用以确定是否可以开始。如果具有以下要求的任何或全部要求,请实现此方法
indexPath
的项是要移动的项。重要提示:是否可以移动项在这里确定。是否可以替换项在 canDisplaceItemAtIndexPath:
方法中确定。如果项既不能移动也不能替换,则需要实现这两个方法。如果不想开始此交换事务,则返回 NO。如果返回 YES,则可以安全地假设交换事务将开始。如果没有实现,交换控制器假设为 YES。
- (BOOL) exchangeController:(SSCollectionViewExchangeController *)exchangeController
canDisplaceItemAtIndexPath:(NSIndexPath *)indexPathOfItemToDisplace
withItemBeingDraggedFromIndexPath:(NSIndexPath *)indexPathOfItemBeingDragged;
如果实现,则在整个交换事务中调用,以确定是否可以交换这两个项。如果你的集合视图中包含完全不能交换的项,或者可能存在无法与被拖动的特定项交换的被替换项的情况,则需要实现此方法。如果没有实现,则默认为 YES。
- (UIView *) exchangeController:(SSCollectionViewExchangeController *)exchangeController
viewForCatchRectangleForItemAtIndexPath:(NSIndexPath *)indexPath;
如果您的集合视图中只可以在长按发生在一个特定矩形上时捕获单元格,则实现此方法并返回表示该矩形的视图。如果此方法没有实现,则假定捕获矩形为整个单元格。
- (UIView *)exchangeController:(SSCollectionViewExchangeController *)exchangeController
snapshotForCell:(UICollectionViewCell *)cell;
SSCollectionViewExchangeController
实现了一个默认方法来使用默认背景色和透明度创建单元格快照。如果这不满足您的要求,则实现此委托方法。在实现此方法之前,请记住,默认快照方法中使用的背景颜色和透明度属性是公开的。考虑在实现此方法之前设置这些属性。
- (void)animateCatchForExchangeController:(SSCollectionViewExchangeController *)exchangeController
withSnapshot:(UIView *)snapshot;
- (void)animateReleaseForExchangeController:(SSCollectionViewExchangeController *)exchangeController
withSnapshot:(UIView *)snapshot // this is the view the user has been dragging
toPoint:(CGPoint)centerOfCell // this is the center of the cell where the release occurred
originalIndexPathForDraggedItem:(NSIndexPath *)originalIndexPathForDraggedItem // animate the alpha for the cell at this index path back to 1.0
completionBlock:(PostReleaseCompletionBlock)completionBlock; // you must execute this completion block in your final completion block
为了向用户提供反馈,SSCollectionViewExchangeController
实现了捕获和释放的默认动画。如果这些实现不满足您的要求,则实现其中一个或两个委托方法。
如果您实现了 animateReleaseForExchangeController
方法,则应执行以下操作
snapshot
渲染到 centerOfCell
。originalIndexPathForDraggedItem
中单元格的 alpha
渲染回 1.0。invalidateLayout
或从其父视图中移除快照。completionBlock
并传递一个动画持续时间。 completionBlock
管理交换事务最后时刻的顺序。首先,它设置一些内部状态,然后调用 invalidateLayout
,这样就会取消隐藏隐藏的单元格(用户将其拖动到的地方)。取消隐藏立即发生,没有任何动画。但是,故意的,用户拖动的快照仍然在视图上,所以单元格的立即取消隐藏发生在快照的后面,所以没有变化可见。然后 completionBlock
根据您提供的持续时间将快照的透明度渲染为 0.0,根据您提供的持续时间,揭示现在取消隐藏的单元格。当这个动画完成后,它会从集合视图中移除快照。 ...
} completion:^(BOOL finished) {
completionBlock(duration);
}];
- (instancetype)initWithDelegate:(id<SSCollectionViewExchangeControllerDelegate>)delegate
collectionView:(UICollectionView *)collectionView;
这是指定的初始化器。 delegate
,通常是您的视图控制器,必须遵守 SSCollectionViewExchangeControllerDelegate
协议。collectionView
是交换控制器将管理的集合视图。
- (UICollectionViewFlowLayout *)layout;
此方法提供方便。委托可以要求其集合视图获取其布局,但这将返回一个 UICollectionViewLayout
,在配置之前需要将其转换为 UICollectionViewFlowLayout
。此方法方便地将布局作为 UICollectionViewFlowLayout
返回,准备好配置。
@property (weak, nonatomic, readonly) UILongPressGestureRecognizer *longPressGestureRecognizer;
// exposed to allow the delegate to set its properties as required
// by default minimumPressDuration is 0.15 and delaysTouchesBegan is YES
@property (nonatomic) CGFloat alphaForDisplacedItem;
// to distinguish the displaced item, default: 0.60
@property (nonatomic) NSTimeInterval animationDuration;
// the duration of each segment of the default animations, default: 0.20
@property (nonatomic) CGFloat blinkToScaleForCatch;
// default: 1.20
@property (nonatomic) CGFloat blinkToScaleForRelease;
// default: 1.05
@property (nonatomic) CGFloat snapshotAlpha;
// default: 0.80
@property (strong, nonatomic) UIColor *snapshotBackgroundColor;
// if you set to nil, no background color will be applied, default: [UIColor darkGrayColor]
@property (nonatomic, readonly) BOOL exchangeTransactionInProgress;
// allows clients to determine if there is an exchange transaction in progress
@property (nonatomic) double animationBacklogDelay;
// When the long press is cancelled, for example by an incoming call, depending on the
// velocity there may be move animations in progress and pending. Without a delay, the
// backlog of animations can still be executing when the exchange controller calls
// reloadData. This prevents reloadData from working properly and restoring the
// collection view to its previous state. The delay allows the backlog of animations
// to complete before the exchange controller cancels the exchange. The default is 0.50
// and should be sufficient in most cases but is exposed in case that doesn't meet
// your requirements.
PaddlesUp! Coach 使用 SSCollectionViewExchangeController 来帮助实现其龙舟队阵容编辑器。你可以在这里看到它的实际应用 这里。
LXReorderableCollectionViewFlowLayout:https://github.com/lxcid/LXReorderableCollectionViewFlowLayout
你可以克隆或下载仓库以使用演示应用程序。它锻炼了一些但并非所有 SSCollectionViewExchangeController
的功能。
演示应用程序包含了一组针对 exchangeObjectInArray:atIndex:withObjectInOtherArray:atIndex:
方法的测试。如果你将测试文件复制到项目中,你可能需要配置项目以便测试目标可以识别文件。