前言: 之前见过瀑布流中的cell,由手势拖动变换位置的效果,一直觉得很炫很神奇。今天无意在破船之家看到了这种效果的实现方法。最核心的位置变换只需掉俩个方法,然后由系统完成。所以很简单,没有想象中的那么复杂。
我们先从简单的Tableview开始。
效果如下
实现过程
- 给tableview添加长按手势。
- 在手势开始时,获取选中的cell,对cell截图。cell隐藏,截图的frame和cell的frame相同,也就是用截图替换掉cell。
- 为了良好的用户体验,将截图放大一点,并设置上阴影效果,让截图跃然于纸上。
- 手势移动时,移动截图。
- 移动数据源中数据位置,
- 移动cell的位置。调用这个方法。
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
- 手势结束时,移除截图,让cell重新显示出来。当然在这个过程中可以来点动画,让效果更炫点。
代码
添加长按手势的代码就不列出来了,这里是处理手势事件的方法。
- (void)handleLongPress:(UILongPressGestureRecognizer *)longPress
{
CGPoint pressLocation = [longPress locationInView:self.tableView];
NSIndexPath *currentIndexPath = [self.tableView indexPathForRowAtPoint:pressLocation];
static NSIndexPath *sourceIndexPath = nil;
static UIView *snapShot = nil;
if (!currentIndexPath) return;
if (longPress.state == UIGestureRecognizerStateBegan)
{
if (currentIndexPath) {
// 0. 记录选中的indexPath
sourceIndexPath = currentIndexPath;
// 1. 截取cell上的图片
UITableViewCell *sourceCell = [self.tableView cellForRowAtIndexPath:sourceIndexPath];
snapShot = [self snapShotInView:sourceCell];
snapShot.frame = sourceCell.frame;
snapShot.alpha = 0;
[self.tableView addSubview:snapShot];
// 2. 对截图做动画,并隐藏cell
[UIView animateWithDuration:0.5 animations:^{
snapShot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapShot.alpha = 0.9;
sourceCell.alpha = 0.0f;
CGPoint o = snapShot.center;
o.y = pressLocation.y;
snapShot.center = o;
} completion:^(BOOL finished) {
sourceCell.hidden = YES;
}];
}
}
else if (longPress.state == UIGestureRecognizerStateChanged)
{
// 移动截图
CGPoint o = snapShot.center;
o.y = pressLocation.y;
snapShot.center = o;
if (![currentIndexPath isEqual:sourceIndexPath]) {
// 数据交换
[self.datas exchangeObjectAtIndex:currentIndexPath.row withObjectAtIndex:sourceIndexPath.row];
// cell移动
[self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:currentIndexPath];
// 更新indexPath
sourceIndexPath = currentIndexPath;
}
}
else if (longPress.state == UIGestureRecognizerStateEnded)
{
// 截图动画消失,并且显示cell
UITableViewCell *sourceCell = [self.tableView cellForRowAtIndexPath:sourceIndexPath];
sourceCell.hidden = NO;
sourceCell.alpha = 0.0f;
[UIView animateWithDuration:0.25 animations:^{
snapShot.center = sourceCell.center;
snapShot.transform = CGAffineTransformIdentity;
sourceCell.alpha = 1.0f;
} completion:^(BOOL finished) {
[snapShot removeFromSuperview];
}];
}
}
- (UIView *)snapShotInView:(UIView *)inputView
{
UIView *snapShot = [inputView snapshotViewAfterScreenUpdates:NO];
snapShot.bounds = inputView.bounds;
snapShot.layer.shadowOffset = CGSizeMake(-5, 0);
snapShot.layer.shadowOpacity = 1;
snapShot.layer.shadowRadius = 5.0;
return snapShot;
}
- if (!currentIndexPath) return; 因为如果选到了section的段头上,currentIndexPath是空值。所以要判断。
- sourceIndexPath 表示的是交换后源cell的索引。也就是cell移动到了哪个位置上。
- 移动的核心代码就这俩句,[self.datas exchangeObjectAtIndex:currentIndexPath.row withObjectAtIndex:sourceIndexPath.row];和[self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:currentIndexPath];先更新数据源,然后更新UI,这个没什么好讲的了。最后别忘记sourceIndexPath = currentIndexPath;移动后源cell的位置也发生变化,所以要更新哦。
CollectionView和上面的过程几乎一模一样。
附上效果图