iOS仿探探左右滑動(dòng)

在陌陌探探等交友軟件中,有個(gè)很經(jīng)典的左滑喜歡,右滑下一個(gè)的效果。還有個(gè)撤銷上一次不喜歡的功能。VIP可以無限撤銷。工作有這個(gè)需求,就搜了下,明白了思路,就自己動(dòng)手敲一遍,理清思路,修改成適合自己使用的。記錄一下過程,省的時(shí)間長(zhǎng)了就忘了。

先上效果圖:


滑動(dòng)效果.gif

核心:拖動(dòng)手勢(shì)的處理,在拖動(dòng)過程中改變各個(gè)view位置和大小。主要是計(jì)算center.y的改變。

代碼實(shí)現(xiàn)過程中,各個(gè)圖片的間距、縮放大小、圖片大小需要經(jīng)常使用,所以設(shè)置了宏,方便使用和修改。
并使用一個(gè)全局變量數(shù)組,存放4個(gè)圖片對(duì)象,方便處理數(shù)據(jù)。
設(shè)置兩個(gè)全局變量分別記錄最上層圖片和最下層圖片。

@property (nonatomic, strong) NSMutableArray *cards;
@property (nonatomic, strong) UIView *topCard; //最上面
@property (nonatomic, strong) UIView *bottomCard; //最底部

#define ImageWidth 200
#define ImageHeight 300
#define ImageScale 0.1 //每張圖片初始化縮小尺寸
#define ImageSpace 20 //每張圖片底部距離

過程分析:

一、初始化。

可以看到默認(rèn)顯示3張圖片。拖動(dòng)第一張過程中,其他2張往上移動(dòng),移動(dòng)過程中逐步變大。最后一張沒變化。所以總共需要初始化4個(gè)圖片。
由于默認(rèn)顯示3張,第3張和第4張的大小位置相同。
每張圖片初始化后,就加載顯示,并調(diào)用sendSubviewToBack方法,把新建的對(duì)象放到最下層,后面的圖片根據(jù)for循環(huán)里初始化代碼,位置越來越靠下,并且越來越小。初始化完成。

    for (int i = 0; i < 4; i++) {
        
        UIView *card = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ImageWidth, ImageHeight)];
        card.tag = 100 + i;
        
        int index = i;
        if (index == 3) {
            index = 2;
        }
       // y坐標(biāo) = 屏幕中心點(diǎn)+中心點(diǎn)下移的距離+每個(gè)圖片距離第一張圖片的間距
       // 圖片高度*縮放比例 = 圖片減小的總大小。除以2是中心下移的距離,就可以和第一張圖片底部對(duì)齊
        card.center = CGPointMake(ScreenW/2, ScreenH/2 +(ImageHeight*ImageScale*index/2)+ ImageSpace*index);
        card.transform = CGAffineTransformMakeScale(1-ImageScale*index, 1-ImageScale*index);
        card.backgroundColor = self.dataArr[i];
        
        [self.cards addObject:card];
        
        [self.view addSubview:card];
        [self.view sendSubviewToBack:card];
        
        //添加拖動(dòng)手勢(shì)
        UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panHandle:)];
        [card addGestureRecognizer:pan];
        
        card.userInteractionEnabled = NO;
        if (i == 0) {
            card.userInteractionEnabled = YES;
            self.topCard = card;
        }else if (i == 3){
            self.bottomCard = card;
        }
    }

二、拖動(dòng)過程中

先處理拖動(dòng)的圖片。由于第一張沒有縮放,拖動(dòng)過程中也不需要改變大小,所以只考慮位置,改變中心點(diǎn)即可。
根據(jù)translationInView:方法得到手指拖動(dòng)移動(dòng)的距離,改變center就行了。調(diào)用setTranslation:CGPointZero重置拖動(dòng)距離,每次計(jì)算完畢改完center就從新開始。
其中,往左往后滑動(dòng),最多也就拖動(dòng)半個(gè)屏幕的距離,所以根據(jù)屏幕的一半計(jì)算出拖動(dòng)距離在半個(gè)屏幕中的比例XOffPercent。從0到1。是0就說明沒移動(dòng),其他的圖片不要改變,是1的說明拖動(dòng)結(jié)束,當(dāng)前圖片移出屏幕,第2張大小和位置變成第一張的大小和位置,其他圖片依次類推。

        CGPoint transLcation = [pan translationInView:cardView];
        cardView.center = CGPointMake(cardView.center.x + transLcation.x, cardView.center.y + transLcation.y);
        CGFloat XOffPercent = (cardView.center.x-ScreenW/2.0)/(ScreenW/2.0);
        CGFloat rotation = M_PI_2/4*XOffPercent;
        cardView.transform = CGAffineTransformMakeRotation(rotation);
        [pan setTranslation:CGPointZero inView:cardView];

根據(jù)滑動(dòng)比例XOffPercent,處理其他圖片的大小和位置。
拖動(dòng)過程中調(diào)用。
一個(gè)for循環(huán),除去第一張和最后一張,改變中間兩張的大小。主要是center的y值比較麻煩。同時(shí)更改圖片的選擇角度,看起來美觀一些。

這個(gè)高度計(jì)算是核心,這個(gè)搞明白了,其他的都不是問題了。
主要就是原始位置減去根據(jù)滑動(dòng)比例引起的位置改變。
其中XOffPercent*ImageSpace代表每張圖片只需要移動(dòng)一個(gè)間隔的距離。
(ImageHeight*ImageScale*index/2)*XOffPercent/index,中心點(diǎn)需要移動(dòng)的距離,最后除以index,代表只需要移動(dòng)到相鄰中心點(diǎn)的距離。
剛做的時(shí)候,沒除index,效果不對(duì),想了好大一會(huì)才明白問題出哪了。

- (void)animationBlowViewWithXOffPercent:(CGFloat)XOffPercent {
    
    for (UIView *card in self.cards) {
        
        if (card != self.topCard && card.tag != 103) {
            
            NSInteger index = card.tag-100;
            
            card.center = CGPointMake(ScreenW/2,ScreenH/2
                                      + (ImageHeight*ImageScale*index/2)
                                      + ImageSpace*index  //上面3行是原始位置,下面2行是改變的大小
                                      - XOffPercent*ImageSpace
                                      - (ImageHeight*ImageScale*index/2)*XOffPercent/index);
            
            CGFloat scale = 1-ImageScale*index + XOffPercent*ImageScale;
            card.transform = CGAffineTransformMakeScale(scale, scale);
        }
    }   
}

三、拖動(dòng)結(jié)束

根據(jù)距離屏幕的距離自行判斷是否需要移除圖片。
如果不需要移除,就用UIView動(dòng)畫把第一張圖片恢復(fù)成原始狀態(tài),并調(diào)用animationBlowViewWithXOffPercent方法處理其他的圖片,滑動(dòng)比例是0。表示其他圖片也恢復(fù)成原始狀態(tài)。
如果需要移除,滑動(dòng)比例是1。最后更改第一張圖片和最后一張圖片的值,重置每張圖片的tag值等,沒什么好說的

-(void)cardRemove{
    
//    滑動(dòng)結(jié)束第一張和放到數(shù)組最后
    [self.cards removeObject:self.topCard];
    [self.cards addObject:self.topCard];
    
//    重新設(shè)置tag
    for (int i = 0 ; i < 4; i++) {
        UIView *card = self.cards[i];
        card.tag = 100+i;
    }
    
    
//    重置第一張和最后一張(第4)
    self.topCard.userInteractionEnabled = NO;
    self.topCard.center = CGPointMake(ScreenW/2, ScreenH/2 + (ImageHeight*ImageScale*2/2) + ImageSpace*2);
    self.topCard.transform = CGAffineTransformMakeScale(1-ImageScale*2, 1-ImageScale*2);
    [self.view sendSubviewToBack:self.topCard];
    
    self.bottomCard = self.topCard;
    
    UIView *currentCard = self.cards.firstObject;
    currentCard.userInteractionEnabled = YES;
    self.topCard = currentCard;   
}

四、點(diǎn)擊按鈕滑動(dòng)

和拖動(dòng)類似,就是UIView動(dòng)畫,改變第一張的位置,同時(shí)更改其他圖片的大小和位置,原理一樣。最后重置tag,更改第一張和最后一張全局變量的值.
計(jì)算y的值還是關(guān)鍵。

        [UIView animateWithDuration:0.5 animations:^{
            
            self.topCard.center = CGPointMake(-ImageWidth, ScreenH/2+50);
            self.topCard.transform = CGAffineTransformMakeRotation(-M_PI_4/2);
            
            for (UIView *card in self.cards) {
                
                if (card.tag != 100 && card.tag != 103) {
                    
                    NSInteger index = card.tag - 100;
                    
                    card.center = CGPointMake(ScreenW/2, ScreenH/2
                                              + ImageHeight*index*ImageScale/2
                                              + ImageSpace*index //原始位置
                                              - ImageSpace
                                              - (ImageHeight*ImageScale*index/2)/index);
                    
                    CGFloat scale = 1-index*ImageScale + ImageScale;
                    card.transform = CGAffineTransformMakeScale(scale, scale);
                }
            }
            
        }completion:^(BOOL finished) {
            
            sender.userInteractionEnabled = YES;
            [self cardRemove];
        }];

移除第一張,變成最后一張。更改數(shù)組里對(duì)象的位置和tag。


-(void)cardRemove{
    
//    滑動(dòng)結(jié)束第一張和放到數(shù)組最后
    [self.cards removeObject:self.topCard];
    [self.cards addObject:self.topCard];
    
//    重新設(shè)置tag
    for (int i = 0 ; i < 4; i++) {
        UIView *card = self.cards[i];
        card.tag = 100+i;
    }
    
//    重置第一張和最后一張(第4)
    self.topCard.userInteractionEnabled = NO;
    self.topCard.center = CGPointMake(ScreenW/2, ScreenH/2 + (ImageHeight*ImageScale*2/2) + ImageSpace*2);
    self.topCard.transform = CGAffineTransformMakeScale(1-ImageScale*2, 1-ImageScale*2);
    [self.view sendSubviewToBack:self.topCard];
    
    self.bottomCard = self.topCard;
    
    UIView *currentCard = self.cards.firstObject;
    currentCard.userInteractionEnabled = YES;
    self.topCard = currentCard;
}

五、撤銷返回

就是把最后一張變成第一張,第一張變成第二張。其他的圖片中心往上移動(dòng),同時(shí)變大。
最后重置tag,更改第一張和最后一張全局變量的值。
過程參考第四部,實(shí)現(xiàn)過程類似,就不貼了。

OVER

DEMO

最后編輯于
?著作權(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)容