拖動(dòng)并交換兩個(gè)View位置的一種思路

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é)束后,重新展示出fromViewtoView;
    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è)輪子;
  • 我! 看! 好! 西! 班! 牙! 奪! 冠!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容