iOS7应用程序管理器中的UICollectionView“滑动删除”?

9

在任何2014年及以后的iPhone或iPad上,双击home键即可查看“应用程序管理器”

enter image description here

这是一个左右滚动的UICollectionView,但它具有“划走”手势..向上滑动。如何实现?从UICollectionView中“删除”单元格并不容易。


对于"剥离"、"撕裂"集合视图中一个单元格的一般问题,这里提供了一个完整而详细的解释:https://stackoverflow.com/a/24339705/294884 希望能帮到有需要的人。


内部来说,这不是一个集合视图。它是由控制器管理的一系列滚动视图。事实上,我正在开发类似的东西,但目前还没有公布任何消息。 - Léo Natan
Joe,你可以将调试器附加到模拟器上的SpringBoard并检查视图层次结构。 - Léo Natan
嗯...这听起来很容易,当你说的时候 :) 不过还是谢谢,我会深入研究的!你能把最后一条评论作为答案吗?因为它非常有用? - Fattie
不管是一个水平的UICollectionView还是一个水平的UITableView...在我看来,使用TableView通过UITableViewCell的插入和删除更容易处理修改和转换。也许这可以帮助你? - Lepidopteron
如果我没记错的话,有两个水平滚动视图,一个是卡片的,另一个是图标的,每个卡片实际上都是一个水平滚动视图。因此,当你“滑动以移除”时,实际上是滚动滚动视图的内容,当达到一定的内容偏移量时,应用程序会关闭并移动其他项目。 - Léo Natan
显示剩余3条评论
2个回答

14

实际上,它比你的问题评论所建议的要简单得多。

您的单元格应包含一个视图(您要拖动的内容),并向该视图添加UIPanGestureRecognizer。

在手势的操作方法中,您可以将视图向上或向下移动,并且当它足够远时,您想要删除它,只需将其动画化即可。这里有很多处理此部分的问题。

这会在您的集合中留下空白,现在您需要移动东西。事实证明,这非常简单:

[_collectionView performBatchUpdates:^{
   [_collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
     // you might want to remove the data from the data source here so the view doesn't come back to life when the collection view is reloaded.
}];

右侧移除的单元格会滑动过来,一切都很好。

另一个需要解决的问题是确保您的手势识别器和集合视图的手势识别器能够良好地协作。幸运的是,这也不太复杂。

[_collectionView.panGestureRecognizer requireGestureRecognizerToFail:pgr]; //where pgr is the recognizer you made for dragging the view off

这意味着为了让集合视图的平移手势正常工作,你的手势必须失败。因此,你要设置自己的手势只在上下平移时起作用,而让集合视图在左右平移时仍然正常工作。在你的手势识别器的代理中,实现以下方法,简单地检查你是否在 x 轴或 y 轴上移动更多。

-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
    CGPoint translation =[gestureRecognizer translationInView:self.view];

    return(translation.x * translation.x > translation.y * translation.y);
}

1
当我使用这种方法时,我使用的集合视图占据了整个屏幕的大小,但我相信你可以告诉集合视图不要裁剪,它就不会。我会测试一下并稍后回报。 - Michael Behan
1
没问题,我刚试了一下,在Storyboard中取消勾选单元格的“剪裁子视图”选项就可以了。 - Michael Behan
1
如果您是在没有使用Storyboard的情况下以编程方式进行设置,那么您还可以在视图的层上将masksToBounds设置为YES。当我实现一个集合视图并需要将项目从cv拖到另一个视图上方时,我自己也这样做了。值得注意的是,如果需要,您可以从performBatchUpdates:completion:中进行动画处理。 - greymouser

1

我正在寻找这个功能,并使用@mbehan的建议,我使用了UICollectionView来模拟这个功能。

我的做法是在集合视图单元格上添加一个较小尺寸的视图(透明背景),并在CollectionView上添加一个单一的平移手势(不是在每个单元格上),然后在平移手势上移动视图,看起来像单元格在移动。当视图到达某个点时,我首先隐藏它,然后删除集合视图单元格。

单元格层次结构:collectionViewCell -> View(标签值==2) -> UILabel(标签值==1) 标签只用于占位符目的。

我在下面发布我的代码:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewCell *cell = (UICollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"Cards" forIndexPath:indexPath];
    UILabel *lblNumber = (UILabel*)[cell.contentView viewWithTag:1];
    UIView *viewTouch = (UIView*)[cell.contentView viewWithTag:2];
    [viewTouch setHidden:NO];
    [lblNumber setText:arrCards[indexPath.row]];

    return cell;
}


- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 50, 0, 30);
  }

-(BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{

    if([gestureRecognizer isEqual:panGesture]) {
    CGPoint point = [(UIPanGestureRecognizer*)gestureRecognizer translationInView:collectionView_];
        if(point.x != 0) { //adjust this condition if you want some leniency on the X axis
        //The translation was on the X axis, i.e. right/left,
        //so this gesture recognizer shouldn't do anything about it
        return NO;
        }
   }
   return YES;
}

- (IBAction)panGestureCalled:(UIPanGestureRecognizer *)sender {
    yFromCenter = [sender translationInView:collectionView_].y; //%%% positive for up, negative for down

    UIView *view = sender.view;
    CGPoint location = [view.superview convertPoint:view.center toView:collectionView_];
    NSIndexPath *indexPath = [collectionView_ indexPathForItemAtPoint:location];
    UICollectionViewCell *cell = [collectionView_ cellForItemAtIndexPath:indexPath];
    UIView *touchView = (UIView*)[cell.contentView viewWithTag:2];


    switch (sender.state) {
      case UIGestureRecognizerStateBegan:{
        originalPoint = touchView.center;
        break;
    };
      case UIGestureRecognizerStateChanged:{
        touchView.center = CGPointMake(originalPoint.x , originalPoint.y + yFromCenter);

        break;
    };
        //%%% let go of the card
      case UIGestureRecognizerStateEnded: {
        CGFloat velocityY = (0.2*[(UIPanGestureRecognizer*)sender velocityInView:collectionView_].y);

        if (velocityY < -30 && yFromCenter<0) {
            [self hideView:touchView withDuration:0.2 andIndexPath:indexPath];

        }else if ((yFromCenter< 0 && yFromCenter > -200) || yFromCenter > 0){

            CGFloat animationDuration = (ABS(velocityY)*.0002)+.2;
            [self resettleViewToOriginalPosition:touchView andDuration:animationDuration];

        }else
            [self hideView:touchView withDuration:0.2 andIndexPath:indexPath];

    };
        break;
      case UIGestureRecognizerStatePossible:break;
      case UIGestureRecognizerStateCancelled:break;
      case UIGestureRecognizerStateFailed:break;
  }
}


-(void)resettleViewToOriginalPosition:(UIView*)view andDuration:(float)duration{
[UIView animateWithDuration:duration
                      delay:0.0f
                    options: UIViewAnimationOptionCurveEaseOut
                 animations:^
 {
     [view setCenter:originalPoint];
 }
                 completion:^(BOOL finished)
 {

 }];
}
- (void)hideView:(UIView*)view withDuration:(float)duration andIndexPath:(NSIndexPath*)indexPath
{

[UIView animateWithDuration:duration
                      delay:0.0f
                    options: UIViewAnimationOptionCurveEaseOut
                 animations:^
 {
     CGRect frame = view.frame;
     frame.origin.y = -300;
     view.frame = frame;
 }
                 completion:^(BOOL finished)
 {
     [view setHidden:YES];
     CGRect frame = view.frame;
     frame.origin.y = 39;
     view.frame = frame;
     NSLog(@"View is hidden.");

     [arrCards removeObjectAtIndex:indexPath.row];
     [collectionView_ performBatchUpdates:^{
         [collectionView_ deleteItemsAtIndexPaths:@[indexPath]];
     } completion:^(BOOL finished) {
         // you might want to remove the data from the data source here so the view doesn't come back to life when the collection view is reloaded.
     }];
 }];
}

将CollectionView的pagingEnabled属性设置为NO,然后就可以了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接