在iOS中移动UICollectionView的单元格?

时间:2015-11-09 09:39:49

标签: ios objective-c cocoa-touch

我有一个UICollectionView。我试图将它作为SpringBoard功能。我能够为每个单元格提供摇动动画。但是我想要当图标抖动时我应该能够移动它们同样。

为了摇动细胞,我在每个细胞上添加了UILongPressGesture。当手势结束时,我在它们上面添加了一个自定义动画&还在左上角添加了一个删除按钮。

长按手势代码:

declaration of variables

CGPoint p;
UILongPressGestureRecognizer *lpgr;
NSIndexPath *gesture_indexPath;

向集合视图添加手势

 lpgr
    = [[UILongPressGestureRecognizer alloc]
       initWithTarget:self action:@selector(handleLongPress:)];
        lpgr.minimumPressDuration = .3; // To detect after how many seconds you want shake the cells
        lpgr.delegate = self;
        [self.collection_view addGestureRecognizer:lpgr];

    lpgr.delaysTouchesBegan = YES;

回调方法

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state != UIGestureRecognizerStateEnded)
    {
        return;
    }
    p = [gestureRecognizer locationInView:self.collection_view];

    NSIndexPath *indexPath = [self.collection_view indexPathForItemAtPoint:p];
    if (indexPath == nil)
    {
        NSLog(@"couldn't find index path");
    }
    else
    {
        [[NSUserDefaults standardUserDefaults]setValue:@"yes" forKey:@"longPressed"];
        [self.collection_view reloadData];

    }

}

固定路径的项目单元格

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"arr_album index row");
    BlogAlbumCell  *cell;
    static NSString *identifier = @"UserBlogAlbum";
    cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    UserAlbum *user_allbum=[arr_userAlbums objectAtIndex:indexPath.row];
    cell.label_blog_name.text=user_allbum.album_name;
    cell.image_blog_image.image = [UIImage imageNamed:@"more.png"];
    [cell.image_blog_image setImageWithURL:[NSURL URLWithString:[IMAGE_BASE_URL stringByAppendingString:user_allbum.album_image]]];
    if([[[NSUserDefaults standardUserDefaults]valueForKey:@"longPressed"] isEqualToString:@"yes"])
    {
        CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        [anim setToValue:[NSNumber numberWithFloat:0.0f]];
        [anim setFromValue:[NSNumber numberWithDouble:M_PI/50]];
        [anim setDuration:0.1];
        [anim setRepeatCount:NSUIntegerMax];
        [anim setAutoreverses:YES];
        cell.layer.shouldRasterize = YES;
        [cell.layer addAnimation:anim forKey:@"SpringboardShake"];
        CGFloat delButtonSize = 20;

        UIButton *delButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, delButtonSize, delButtonSize)];
        delButton.center = CGPointMake(9, 10);
        delButton.backgroundColor = [UIColor clearColor];
        [delButton setImage: [UIImage imageNamed:@"cross_30.png"] forState:UIControlStateNormal];
        [cell addSubview:delButton];
        [delButton addTarget:self action:@selector(deleteRecipe:) forControlEvents:UIControlEventTouchUpInside];
    }
    else if ([[[NSUserDefaults standardUserDefaults]valueForKey:@"singleTap"] isEqualToString:@"yes"])
    { 
        for(UIView *subview in [cell subviews])
        {
            if([subview isKindOfClass:[UIButton class]])
            {
                [subview removeFromSuperview];
            }
            else
            {
                // Do nothing - not a UIButton or subclass instance
            }
        }
        [cell.layer removeAllAnimations];
        // _deleteButton.hidden = YES; 
        // [_deleteButton removeFromSuperview];
    }
        return cell;
}

直到这里才能正常工作。

为了移动单元格,我制作了一个示例应用程序,其中我添加了UICollectionViewController&覆盖此方法

-(void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{

    NSLog(@"Move at index path called");
}

这也很好用。它还使用长按手势&当手势被剔除然后我能够移动细胞。但现在问题是在一个我可以移动单元格或动画他们。如果我添加我的自定义手势然后我无法移动图像。请告诉我我怎么能删除此问题?

1 个答案:

答案 0 :(得分:8)

看看这个名为DragDropCollectionView的项目。它实现了拖放和动画。

编辑:此问题可以分解为2个较小的子问题:

  1. 如何为细胞设置动画
  2. 如何使用拖放重新排序单元格。
  3. 您应该将这两个解决方案组合到UICollectionView的子类中,以获得您的主要解决方案。

    如何为细胞设置动画

    要获得摆动动画,您需要添加2种不同的动画效果:

    1. 垂直移动单元格,即上下反弹
    2. 旋转单元格
    3. 最后,为每个单元格添加一个随机间隔时间,这样看起来单元格不会均匀动画
    4. 以下是代码:

      @interface DragDropCollectionView ()
      @property (assign, nonatomic) BOOL isWiggling;
      @end
      
      @implementation DragDropCollectionView
      
      //Start and Stop methods for wiggle
      - (void) startWiggle {
          for (UICollectionViewCell *cell in self.visibleCells) {
              [self addWiggleAnimationToCell:cell];
          }
          self.isWiggling = true;
      }
      
      - (void)stopWiggle {
          for (UICollectionViewCell *cell in self.visibleCells) {
              [cell.layer removeAllAnimations];
          }
          self.isWiggling = false;
      }
      
      //- (UICollectionViewCell *)dequ
      
      - (UICollectionViewCell *)dequeueReusableCellWithReuseIdentifier:(NSString *)identifier forIndexPath:(nonnull NSIndexPath *)indexPath{
          UICollectionViewCell *cell = [super dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
          if (self.isWiggling) {
              [self addWiggleAnimationToCell:cell];
          } else {
              [cell.layer removeAllAnimations];
          }
          return [[UICollectionViewCell alloc] init];
      }
      
      //Animations
      - (void)addWiggleAnimationToCell:(UICollectionViewCell *)cell {
          [CATransaction begin];
          [CATransaction setDisableActions:false];
          [cell.layer addAnimation:[self rotationAnimation] forKey:@"rotation"];
          [cell.layer addAnimation:[self bounceAnimation] forKey:@"bounce"];
          [CATransaction commit];
      
      }
      
      - (CAKeyframeAnimation *)rotationAnimation {
          CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
          CGFloat angle = 0.04;
          NSTimeInterval duration = 0.1;
          double variance = 0.025;
          animation.values = @[@(angle), @(-1 * angle)];
          animation.autoreverses = YES;
          animation.duration = [self randomizeInterval:duration withVariance: variance];
          animation.repeatCount = INFINITY;
          return animation;
      }
      
      - (CAKeyframeAnimation *)bounceAnimation {
          CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
          CGFloat bounce = 3.0;
          NSTimeInterval duration = 0.12;
          double variance = 0.025;
          animation.values = @[@(bounce), @(-1 * bounce)];
          animation.autoreverses = YES;
          animation.duration = [self randomizeInterval:duration withVariance: variance];
          animation.repeatCount = INFINITY;
      
          return animation;
      }
      
      - (NSTimeInterval)randomizeInterval:(NSTimeInterval)interval withVariance:(double)variance {
          double randomDecimal = (arc4random() % 1000 - 500.0) / 500.0;
          return interval + variance * randomDecimal;
      }
      

      如何使用拖放重新排序单元格

      所以这个想法是这样的:你没有移动实际的单元格,而是移动UIImageView,其中包含单元格内容的UIImage。

      算法或多或少是这样的。我把它分为3个部分,gestureRecognizerBegan,Changed和Ended

      gestureRecognizerBegan:

      1. 当gestureRecognizer开始时,确定长按是否确实在单元格上(而不是空白区域)
      2. 获取单元格的UIImage(请参阅我的方法“getRasterizedImageOfCell”)
      3. 隐藏单元格(即alpha = 0),使用单元格的确切框架创建UIImageView,这样用户就不会意识到您实际上已隐藏了单元格,而您实际上正在使用imageview。
      4. gestureRecognizerChanged:

        1. 更新UIImageView的中心,使其随手指移动。
        2. 如果用户已经停止移动他的指尖,即他正在他想要替换的单元格上空盘旋,则现在需要交换单元格。 (看看我的函数“shouldSwapCells”,这个方法返回一个是否应该交换单元格的bool)
        3. 将要拖动的单元格移动到新的indexPath。 (看看我的方法“swapDraggedCell”)。 UICollectionView有一个名为“moveItemAtIndexPath:toIndexPath”的内置方法,我不确定UITableView是否有相同的东西
        4. gestureRecognizerEnd:

          1. 将UIImageView“删除”回单元格
          2. 将单元格alpha从0.0更改为1.0,并从视图中删除UIImageView。
          3. 以下是代码:

            @interface DragDropCollectionView ()
            @property (strong, nonatomic) NSIndexPath *draggedCellIndexPath;
            @property (strong, nonatomic) UIImageView *draggingImageView;
            @property (assign, nonatomic) CGPoint touchOffsetFromCenterOfCell;
            @property (strong, nonatomic) UILongPressGestureRecognizer *longPressRecognizer;
            @end
            
            @implementation DragDropCollectionView
            
            - (void)handleLongPress:(UILongPressGestureRecognizer *)longPressRecognizer {
                CGPoint touchLocation = [longPressRecognizer locationInView:self];
                switch (longPressRecognizer.state) {
                    case UIGestureRecognizerStateBegan: {
                        self.draggedCellIndexPath = [self indexPathForItemAtPoint:touchLocation];
                        if (self.draggedCellIndexPath != nil) {
                            UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
                            self.draggingImageView = [[UIImageView alloc] initWithImage:[self rasterizedImageCopyOfCell:draggedCell]];
                            self.draggingImageView.center = draggedCell.center;
                            [self addSubview:self.draggingImageView];
                            draggedCell.alpha = 0.0;
                            self.touchOffsetFromCenterOfCell = CGPointMake(draggedCell.center.x - touchLocation.x, draggedCell.center.y - touchLocation.y);
                            [UIView animateWithDuration:0.4 animations:^{
                                self.draggingImageView.transform = CGAffineTransformMakeScale(1.3, 1.3);
                                self.draggingImageView.alpha = 0.8;
                            }];
                        }
                        break;
                    }
                    case UIGestureRecognizerStateChanged: {
                        if (self.draggedCellIndexPath != nil) {
                            self.draggingImageView.center = CGPointMake(touchLocation.x + self.touchOffsetFromCenterOfCell.x, touchLocation.y + self.touchOffsetFromCenterOfCell.y);
                        }
                        float pingInterval = 0.3;
                        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(pingInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                            NSIndexPath *newIndexPath = [self indexPathToSwapCellWithAtPreviousTouchLocation:touchLocation];
                            if (newIndexPath) {
                                [self swapDraggedCellWithCellAtIndexPath:newIndexPath];
                            }
                        });
                        break;
                    }
                    case UIGestureRecognizerStateEnded: {
                        if (self.draggedCellIndexPath != nil ) {
                            UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:self.draggedCellIndexPath];
                            [UIView animateWithDuration:0.4 animations:^{
                                self.draggingImageView.transform = CGAffineTransformIdentity;
                                self.draggingImageView.alpha = 1.0;
                                if (draggedCell != nil) {
                                    self.draggingImageView.center = draggedCell.center;
                                }
                            } completion:^(BOOL finished) {
                                [self.draggingImageView removeFromSuperview];
                                self.draggingImageView = nil;
                                if (draggedCell != nil) {
                                    draggedCell.alpha = 1.0;
                                    self.draggedCellIndexPath = nil;
                                }
                            }];
                        }
                    }
            
                    default:
                        break;
                }
            }
            
            - (UIImage *)rasterizedImageCopyOfCell:(UICollectionViewCell *)cell {
                UIGraphicsBeginImageContextWithOptions(cell.bounds.size, false, 0.0);
                [cell.layer renderInContext:UIGraphicsGetCurrentContext()];
                UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
                return image;
            }
            
            - (NSIndexPath *)indexPathToSwapCellWithAtPreviousTouchLocation:(CGPoint)previousTouchLocation {
                CGPoint currentTouchLocation = [self.longPressRecognizer locationInView:self];
                if (!isnan(currentTouchLocation.x) && !isnan(currentTouchLocation.y)) {
                    if ([self distanceBetweenPoints:currentTouchLocation secondPoint:previousTouchLocation] < 20.0) {
                        NSIndexPath *newIndexPath = [self indexPathForItemAtPoint:currentTouchLocation];
                        return newIndexPath;
                    }
                }
                return nil;
            }
            
            - (CGFloat)distanceBetweenPoints:(CGPoint)firstPoint secondPoint:(CGPoint)secondPoint {
                CGFloat xDistance = firstPoint.x - secondPoint.x;
                CGFloat yDistance = firstPoint.y - secondPoint.y;
                return sqrtf(xDistance * xDistance + yDistance * yDistance);
            }
            
            - (void)swapDraggedCellWithCellAtIndexPath:(NSIndexPath *)newIndexPath {
                [self moveItemAtIndexPath:self.draggedCellIndexPath toIndexPath:newIndexPath];
                UICollectionViewCell *draggedCell = [self cellForItemAtIndexPath:newIndexPath];
                draggedCell.alpha = 0.0;
                self.draggedCellIndexPath = newIndexPath;
            }
            

            希望这会有所帮助:)