2018.08.13更新:
当当当当,良辰美景冒泡日~
闲暇时分,老夫把Demo用纯代码写了一遍,并且加入了自动滚动,核心思路并没有改变,新的效果图如下:
希望能给小伙伴带来一些帮助~
Demo地址:https://github.com/Calabash-Boy/CB_DragView
父老乡亲们,好久不见,想死你们了~
世界杯正当火热,每天我家天台都熙熙攘攘,人来人往,好生热闹,而我看好的球队不出所料,都一一回家了...
闲话于此,公司最近要做一个需求,客户预定酒店的时候可以自己决定来入住哪个房间,并且和谁一个房间🤩😏,想想果然有些小激动,demo实现的效果如下:
在网上查询了一下,大部分都是tableViewCell的交换,并不能很好的满足这个需求,因此尝试自己写了一种思路;
Demo地址:https://github.com/Calabash-Boy/CB_DragView
如果喜欢,烦请各位大佬赏个小星星😏~
思路如下:
- 给每个可能拖动的View添加一个长按手势;
- 手势触发的时候对该View(称之为
fromView
)进行截图,我们在拖动的过程中也是对这个截图进行操作,并且隐藏fromView
; - 手势结束的时候,判断手势点的位置,有两种情况:
1.在某个可拖动View上,我们称之为toView
,隐藏toView
,对toView
截图并使其截图移动到fromView
的位置,fromView
下沉到toView
的位置,并且交换数据源中相关的数据,更新界面,当两个截图交换的动画结束后,重新展示出fromView
和toView
;
2.不在任何一个可拖动View上,截图回归原位,fromView
显示;
关键代码如下:
- 注意其中坐标转换的代码,要把出发View和落脚View的fram转化到当前操作View的坐标系中;
- (void)longPressGestureRecognized:(UILongPressGestureRecognizer *)longPress {
//首先校验当前的Menu是否可以拖动
CB_MenuView *menu = (CB_MenuView *)longPress.view;
UILabel *dataLabel = [menu viewWithTag:110];
if (!dataLabel.text.length) return;
//当前手指的位置
CGPoint currentPoint = [longPress locationInView:self];
if (longPress.state == UIGestureRecognizerStateBegan) {
//记录刚开始的时候View的位置
//一定要把menu的坐标转换到自己的坐标系上
CGRect rect = [menu.superview convertRect:menu.frame toView:self];
self.fromCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
self.fromView = menu;
self.fromIndex = [_menuViewArray indexOfObject:menu];
[self beginDragAnimation];
}
else if (longPress.state == UIGestureRecognizerStateChanged) {
//拖动过程中移动动画View
[UIView animateWithDuration:0.1 animations:^{
CGPoint screenshotViewCenter = self.screenshotView.center;
screenshotViewCenter.y = currentPoint.y;
self.screenshotView.center = screenshotViewCenter;
}];
}
else {
self.toView = nil;
CGRect rect = CGRectZero;
//拖动结束 查看结束的位置是否处于某个Menu中 如果是 则交换位置
for (CB_MenuView *menu in _menuViewArray) {
rect = [menu.superview convertRect:menu.frame toView:self];
if (CGRectContainsPoint(rect, currentPoint)) {
self.toView = menu;
self.toIndex = [_menuViewArray indexOfObject:menu];
break;
}
}
[self endDragAnimation];
}
}
- 开始动画的代码,注意此处的截图方法是一个分类,demo中可查看;
- (void)beginDragAnimation {
if (self.screenshotView) {
[self.screenshotView removeFromSuperview];
self.screenshotView = nil;
}
//产生拖动的截图 隐藏原有截图
UIView *screenshotView = [self.fromView screenshotViewWithShadowOpacity:0.3 shadowColor:[UIColor blackColor]];
[self addSubview:screenshotView];
[self bringSubviewToFront:screenshotView];
self.screenshotView = screenshotView;
self.screenshotView.center = self.fromCenter;
self.fromView.hidden = YES;
self.screenshotView.transform = CGAffineTransformMakeScale(1.03, 1.03);
}
- 结束动画的代码;
- (void)endDragAnimation {
if (self.toView) {
CGRect rect = [self.toView.superview convertRect:self.toView.frame toView:self];
self.toCenter = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
//截图 然后交换位置
UIView *screenshotToView = [self.toView screenshotViewWithShadowOpacity:0.3 shadowColor:[UIColor blackColor]];
[self addSubview:screenshotToView];
[self insertSubview:screenshotToView belowSubview:self.screenshotView];
self.toView.hidden = YES;
screenshotToView.center = self.toCenter;
screenshotToView.transform = CGAffineTransformMakeScale(1.03, 1.03);
//此时去交换数据 更新界面
[self exchangeData];
//交换位置
[UIView animateWithDuration:0.3 animations:^{
//toView -> fromView
screenshotToView.center = self.fromCenter;
screenshotToView.transform = CGAffineTransformIdentity;
//fromView -> toView
self.screenshotView.transform = CGAffineTransformIdentity;
self.screenshotView.center = self.toCenter;
} completion:^(BOOL finished) {
[screenshotToView removeFromSuperview];
self.toView.hidden = NO;
[self.screenshotView removeFromSuperview];
self.screenshotView = nil;
self.fromView.hidden = NO;
}];
} else {
//不在任何一个位置 返回原处
[UIView animateWithDuration:0.3 animations:^{
self.screenshotView.transform = CGAffineTransformIdentity;
self.screenshotView.center = self.fromCenter;
} completion:^(BOOL finished) {
[self.screenshotView removeFromSuperview];
self.screenshotView = nil;
self.fromView.hidden = NO;
}];
}
}
多说几句:
- 本demo只提供一种交换动画的思路,对数据源的操作以及界面更新请按实际业务进行变更;
- demo中的某些方法只是我为了快速完成测试而写,大家可以在注释处查看;
- 本demo在移动过程中只更改了截图的y值,可以根据实际需要更改x的值;
- 非常感谢下面这篇文章的思路和代码,截图的代码就是出自该demo;
链接地址: https://www.jianshu.com/p/ef0d355f2cb4 - 后续如果有时间会尝试写一个轮子;
- 我! 看! 好! 西! 班! 牙! 夺! 冠!