項目中用到了這個功能装处,感覺挺好玩的,所以就研究總結(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];
}