2018.08.13更新:
當(dāng)當(dāng)當(dāng)當(dāng),良辰美景冒泡日~
閑暇時(shí)分,老夫把Demo用純代碼寫了一遍,并且加入了自動(dòng)滾動(dòng),核心思路并沒有改變,新的效果圖如下:

自動(dòng)滾動(dòng).gif
希望能給小伙伴帶來一些幫助~
Demo地址:https://github.com/Calabash-Boy/CB_DragView
父老鄉(xiāng)親們,好久不見,想死你們了~
世界杯正當(dāng)火熱,每天我家天臺(tái)都熙熙攘攘,人來人往,好生熱鬧,而我看好的球隊(duì)不出所料,都一一回家了...

寶寶心里苦
閑話于此,公司最近要做一個(gè)需求,客戶預(yù)定酒店的時(shí)候可以自己決定來入住哪個(gè)房間,并且和誰一個(gè)房間????,想想果然有些小激動(dòng),demo實(shí)現(xiàn)的效果如下:

drag.gif
在網(wǎng)上查詢了一下,大部分都是tableViewCell的交換,并不能很好的滿足這個(gè)需求,因此嘗試自己寫了一種思路;
Demo地址:https://github.com/Calabash-Boy/CB_DragView
如果喜歡,煩請(qǐng)各位大佬賞個(gè)小星星??~
思路如下:
- 給每個(gè)可能拖動(dòng)的View添加一個(gè)長(zhǎng)按手勢(shì);
- 手勢(shì)觸發(fā)的時(shí)候?qū)υ揤iew(稱之為
fromView)進(jìn)行截圖,我們?cè)谕蟿?dòng)的過程中也是對(duì)這個(gè)截圖進(jìn)行操作,并且隱藏fromView; - 手勢(shì)結(jié)束的時(shí)候,判斷手勢(shì)點(diǎn)的位置,有兩種情況:
1.在某個(gè)可拖動(dòng)View上,我們稱之為toView,隱藏toView,對(duì)toView截圖并使其截圖移動(dòng)到fromView的位置,fromView下沉到toView的位置,并且交換數(shù)據(jù)源中相關(guān)的數(shù)據(jù),更新界面,當(dāng)兩個(gè)截圖交換的動(dòng)畫結(jié)束后,重新展示出fromView和toView;
2.不在任何一個(gè)可拖動(dòng)View上,截圖回歸原位,fromView顯示;
關(guān)鍵代碼如下:
- 注意其中坐標(biāo)轉(zhuǎn)換的代碼,要把出發(fā)View和落腳View的fram轉(zhuǎn)化到當(dāng)前操作View的坐標(biāo)系中;
- (void)longPressGestureRecognized:(UILongPressGestureRecognizer *)longPress {
//首先校驗(yàn)當(dāng)前的Menu是否可以拖動(dòng)
CB_MenuView *menu = (CB_MenuView *)longPress.view;
UILabel *dataLabel = [menu viewWithTag:110];
if (!dataLabel.text.length) return;
//當(dāng)前手指的位置
CGPoint currentPoint = [longPress locationInView:self];
if (longPress.state == UIGestureRecognizerStateBegan) {
//記錄剛開始的時(shí)候View的位置
//一定要把menu的坐標(biāo)轉(zhuǎn)換到自己的坐標(biāo)系上
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) {
//拖動(dòng)過程中移動(dòng)動(dòng)畫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;
//拖動(dòng)結(jié)束 查看結(jié)束的位置是否處于某個(gè)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];
}
}
- 開始動(dòng)畫的代碼,注意此處的截圖方法是一個(gè)分類,demo中可查看;
- (void)beginDragAnimation {
if (self.screenshotView) {
[self.screenshotView removeFromSuperview];
self.screenshotView = nil;
}
//產(chǎn)生拖動(dòng)的截圖 隱藏原有截圖
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);
}
- 結(jié)束動(dòng)畫的代碼;
- (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);
//此時(shí)去交換數(shù)據(jù) 更新界面
[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 {
//不在任何一個(gè)位置 返回原處
[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只提供一種交換動(dòng)畫的思路,對(duì)數(shù)據(jù)源的操作以及界面更新請(qǐng)按實(shí)際業(yè)務(wù)進(jìn)行變更;
- demo中的某些方法只是我為了快速完成測(cè)試而寫,大家可以在注釋處查看;
- 本demo在移動(dòng)過程中只更改了截圖的y值,可以根據(jù)實(shí)際需要更改x的值;
- 非常感謝下面這篇文章的思路和代碼,截圖的代碼就是出自該demo;
鏈接地址: http://www.itdecent.cn/p/ef0d355f2cb4 - 后續(xù)如果有時(shí)間會(huì)嘗試寫一個(gè)輪子;
- 我! 看! 好! 西! 班! 牙! 奪! 冠!