UICollectionView實現(xiàn)長按cell抖動和拖拽移動(支持iOS9以下)

yuandi.gif

項目中用到了這個功能装处,感覺挺好玩的,所以就研究總結(jié)了一下曲掰,寫了個demo大家一起review一下,哈哈哈奈辰。git下載地址

這個效果主要分了兩個部分,一個是抖動和cell的拖拽移動位置乱豆。

  • 抖動
    抖動的動能比較容易實現(xiàn)奖恰,在collection上面加一個長按手勢,然后在響應手勢的時候宛裕,collection刷新一下給所有的cell都加上一個抖動動畫效果瑟啃,然后手勢結(jié)束的時候移除動畫效果就行。
-(void)starAnimation:(BOOL)animaiton
{
    if (animaiton) {
        CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        //抖動的話添加一個旋轉(zhuǎn)角度給他就好
        basicAnimation.fromValue = @(-M_PI_4/14);
        basicAnimation.toValue = @(M_PI_4/14);
        basicAnimation.duration = 0.08;
        basicAnimation.repeatCount = MAXFLOAT;
        basicAnimation.autoreverses = YES;
        [self.layer addAnimation:basicAnimation forKey:nil];
    }else{
        [self.layer addAnimation:[CAAnimation animation] forKey:nil];
    }
}

cell的拖拽移動

這里面主要說一下iOS9以下的實現(xiàn)方法揩尸,因為iOS9以上在collectionview中已經(jīng)有封裝好的方法直接調(diào)用實現(xiàn)該效果
主要的實現(xiàn)思路如下

  • 首先在長按手勢響應的時候蛹屿,獲取到長按的是那個cell,記錄該cell的indexPath岩榆,然后截圖改cell错负,得到一個view坟瓢,把該view加在collectionview上面的該cell的位置,然后把cell隱藏掉犹撒,這時候我們看到的視圖是截圖出來的view折联,實際的cell已經(jīng)隱藏掉
  • 第二步就是移動了。在手勢的響應方法里面狀態(tài)改變的時候识颊,計算手勢位移诚镰,然后更新截圖出來的view的中心點(注意:這里看到的是截圖出來的view了,而不是cell)祥款,接著遍歷collectionview中visibleCells清笨,獲取到可見的cell,稱之為visibleCell刃跛,判斷visibleCell的中心點和移動view的中心點的相交的大小抠艾,如果超過了visibleCell的大小的一半,那么就刷新數(shù)據(jù)源奠伪,把因為我們要把長按的cell替換到該位置跌帐。
  • 第三步手勢結(jié)束的時候就是把截圖的view移除,然后顯示出來我們隱藏掉的cell就OK啦
-(void)pan:(UILongPressGestureRecognizer *)pan
{
    //判斷手勢狀態(tài)
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:{

            self.isBegin = YES;
            [self.collectionView reloadData];
            [self.collectionView performBatchUpdates:^{
                
            } completion:^(BOOL finished) {
                //判斷手勢落點位置是否在路徑上
                self.indexPath = [self.collectionView indexPathForItemAtPoint:[pan locationOfTouch:0 inView:pan.view]];
                //得到該路徑上的cell
                self.cell = [self.collectionView cellForItemAtIndexPath:self.indexPath];
                //截圖cell绊率,得到一個view
                self.tempMoveCell = [self.cell snapshotViewAfterScreenUpdates:NO];
                self.tempMoveCell.frame = self.cell.frame;
                
                 [self.collectionView addSubview:self.tempMoveCell];
                self.cell.hidden = YES;
                //記錄當前手指位置
                _lastPoint = [pan locationOfTouch:0 inView:pan.view];
                }];
            }
            break;
        case UIGestureRecognizerStateChanged:
        {
            //偏移量
            CGFloat tranX = [pan locationOfTouch:0 inView:pan.view].x - _lastPoint.x;
            CGFloat tranY = [pan locationOfTouch:0 inView:pan.view].y - _lastPoint.y;
            
            //更新cell位置
            _tempMoveCell.center = CGPointApplyAffineTransform(_tempMoveCell.center, CGAffineTransformMakeTranslation(tranX, tranY));
             //記錄當前手指位置
            _lastPoint = [pan locationOfTouch:0 inView:pan.view];
            
            for (UICollectionViewCell *cell in [self.collectionView visibleCells]) {
                //剔除隱藏的cell
                if ([self.collectionView indexPathForCell:cell] == self.indexPath) {
                    continue;
                }
                //計算中心谨敛,如果相交一半就移動
                CGFloat spacingX = fabs(_tempMoveCell.center.x - cell.center.x);
                CGFloat spacingY = fabs(_tempMoveCell.center.y - cell.center.y);
                if (spacingX <= _tempMoveCell.bounds.size.width / 2.0f && spacingY <= _tempMoveCell.bounds.size.height / 2.0f){
                    self.moveIndexPath = [self.collectionView indexPathForCell:cell];
                    //更新數(shù)據(jù)源(移動前必須更新數(shù)據(jù)源)
                    [self updateDataSource];
                    //移動cell
                    [self.collectionView moveItemAtIndexPath:self.indexPath toIndexPath:self.moveIndexPath];
                   //設置移動后的起始indexPath
                    self.indexPath = self.moveIndexPath;
                    break;
                }
            }
        }
            break;
        case UIGestureRecognizerStateEnded:
        {
            [self.collectionView performBatchUpdates:^{
                
            } completion:^(BOOL finished) {
                self.cell  = [self.collectionView cellForItemAtIndexPath:self.indexPath];
                [UIView animateWithDuration:0.1 animations:^{
                    _tempMoveCell.center = self.cell.center;
                } completion:^(BOOL finished) {
                    [_tempMoveCell removeFromSuperview];
                    self.cell.hidden = NO;
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        self.isBegin = NO;
                        [self.collectionView reloadData];
                    });
                }];

            }];
        }
            break;
        default:
            break;
    }
}

刷新數(shù)據(jù)源

-(void)updateDataSource
{
    //取出源item數(shù)據(jù)
    id objc =  [[self.numArray objectAtIndex:self.indexPath.section] objectAtIndex:self.indexPath.row];
    //從資源數(shù)組中移除該數(shù)據(jù),不能直接刪除某個數(shù)據(jù),因為有可能有相同的數(shù)據(jù)滤否,一下子刪除了多個數(shù)據(jù)源脸狸,造成clash
//    [[self.numArray objectAtIndex:self.indexPath.section] removeObject:objc];
    
    //刪除指定位置的數(shù)據(jù),這樣就只刪除一個藐俺,不會重復刪除
    
    [[self.numArray objectAtIndex:self.indexPath.section] removeObjectAtIndex:self.indexPath.row];
   //將數(shù)據(jù)插入到資源數(shù)組中的目標位置上
    [[self.numArray objectAtIndex:self.moveIndexPath.section] insertObject:objc atIndex:self.moveIndexPath.row];
}

iOS9.0以后的實現(xiàn)方法

用API中下面的方法即可實現(xiàn)

// Support for reordering
  //開始移動cell
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0); // returns NO if reordering was prevented from beginning - otherwise YES
  //更新cell的位置
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition NS_AVAILABLE_IOS(9_0);
   //結(jié)束
- (void)endInteractiveMovement NS_AVAILABLE_IOS(9_0);
   //取消
- (void)cancelInteractiveMovement NS_AVAILABLE_IOS(9_0);
-(void)pan:(UILongPressGestureRecognizer *)pan
{
    //判斷手勢狀態(tài)
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:{
             //判斷手勢落點位置是否在路徑上
             self.indexPath = [self.collectionView indexPathForItemAtPoint:[pan locationOfTouch:0 inView:pan.view]];
             //開始移動cell
             [self.collectionView beginInteractiveMovementForItemAtIndexPath:self.indexPath];
        }
            break;
        case UIGestureRecognizerStateChanged:
        {
            //更新cell的位置
            [self.collectionView updateInteractiveMovementTargetPosition:[pan locationInView:self.collectionView]];
        }
            break;
        case UIGestureRecognizerStateEnded:
        {
            //結(jié)束
            [self.collectionView endInteractiveMovement];
        }
            break;
        default:
            //取消
            [self.collectionView cancelInteractiveMovement];
            break;
    }
}

接著實現(xiàn)代理的方法跟新數(shù)據(jù)源

#pragma ios9.0以后
-(BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath
{
    return YES;
}
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0)
{
    //取出源item數(shù)據(jù)
    id objc =  [[self.numArray objectAtIndex:sourceIndexPath.section] objectAtIndex:self.indexPath.row];
    
    //刪除指定位置的數(shù)據(jù)炊甲,這樣就只刪除一個,不會重復刪除
    
    [[self.numArray objectAtIndex:sourceIndexPath.section] removeObjectAtIndex:sourceIndexPath.row];
    //    //將數(shù)據(jù)插入到資源數(shù)組中的目標位置上
    [[self.numArray objectAtIndex:destinationIndexPath.section] insertObject:objc atIndex:destinationIndexPath.row];
}
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末欲芹,一起剝皮案震驚了整個濱河市卿啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菱父,老刑警劉巖颈娜,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異浙宜,居然都是意外死亡官辽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門粟瞬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來同仆,“玉大人,你說我怎么就攤上這事裙品∷着” “怎么了俗或?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扶镀。 經(jīng)常有香客問我蕴侣,道長,這世上最難降的妖魔是什么臭觉? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任昆雀,我火速辦了婚禮,結(jié)果婚禮上蝠筑,老公的妹妹穿的比我還像新娘狞膘。我一直安慰自己,他們只是感情好什乙,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布挽封。 她就那樣靜靜地躺著,像睡著了一般臣镣。 火紅的嫁衣襯著肌膚如雪辅愿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天忆某,我揣著相機與錄音点待,去河邊找鬼。 笑死弃舒,一個胖子當著我的面吹牛癞埠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聋呢,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苗踪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了削锰?” 一聲冷哼從身側(cè)響起通铲,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎器贩,沒想到半個月后测暗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡磨澡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了质和。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片稳摄。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖饲宿,靈堂內(nèi)的尸體忽然破棺而出厦酬,到底是詐尸還是另有隱情胆描,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布仗阅,位于F島的核電站昌讲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏减噪。R本人自食惡果不足惜短绸,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望筹裕。 院中可真熱鬧醋闭,春花似錦、人聲如沸朝卒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抗斤。三九已至囚企,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瑞眼,已是汗流浹背龙宏。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留负拟,地道東北人烦衣。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像掩浙,于是被迫代替她去往敵國和親花吟。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容