2012-10-03 36 views
59

我目前正在尝试使用UICollectionView实现UITableView重新排序行为。UICollectionView有效拖放

让我们把一个的UITableView电视UICollectionView CV(澄清以下说明)

我基本上是试图重现电视的拖&下降,但我不使用编辑模式,只要长按手势被触发,单元就准备好被移动。它工作正常,我使用CV的移动方法,一切都很好。

我更新CV的contentOffset属性以处理用户拖动单元格时的滚动。当用户转到顶部和底部的特定矩形时,我会更新contentOffset和CV滚动条。问题是当用户停止移动它的手指时,手势不会发送任何更新,这使得滚动停止并且一旦用户移动他的手指就再次开始。

这种行为是绝对不自然的,我宁愿继续滚动,直到用户释放CV,因为它是在电视中的情况。电视拖动&下降的经验是真棒,我真的想重现相同的感觉。有人知道他们在重新排序期间如何管理电视中的滚动吗?

  • 我试着用一个定时器重复触发一个滚动操作,只要手势位置在正确的位置,滚动非常糟糕,并且效率不高(非常缓慢且很有跳跃性)。
  • 我也尝试使用GCD在另一个线程中聆听手势位置,但结果甚至更糟。

我跑出了关于这个的想法,所以如果有人有答案,我会嫁给他!

这里是长按方法的实现:

- (void)handleLongPress:(UILongPressGestureRecognizer *)sender 
{ 
    ReorganizableCVCLayout *layout = (ReorganizableCVCLayout *)self.collectionView.collectionViewLayout; 
    CGPoint gesturePosition = [sender locationInView:self.collectionView]; 
    NSIndexPath *selectedIndexPath = [self.collectionView indexPathForItemAtPoint:gesturePosition]; 

    if (sender.state == UIGestureRecognizerStateBegan) 
    { 
     layout.selectedItem = selectedIndexPath; 
     layout.gesturePoint = gesturePosition; // Setting gesturePoint invalidate layout 
    } 
    else if (sender.state == UIGestureRecognizerStateChanged) 
    { 
     layout.gesturePoint = gesturePosition; // Setting gesturePoint invalidate layout 
     [self swapCellAtPoint:gesturePosition]; 
     [self manageScrollWithReferencePoint:gesturePosition]; 
    } 
    else 
    { 
     [self.collectionView performBatchUpdates:^ 
     { 
      layout.selectedItem = nil; 
      layout.gesturePoint = CGPointZero; // Setting gesturePoint invalidate layout 
     } completion:^(BOOL completion){[self.collectionView reloadData];}]; 
    } 
} 

为了使CV滚动,我使用的方法:

- (void)manageScrollWithReferencePoint:(CGPoint)gesturePoint 
{ 
    ReorganizableCVCLayout *layout = (ReorganizableCVCLayout *)self.collectionView.collectionViewLayout; 
    CGFloat topScrollLimit = self.collectionView.contentOffset.y+layout.itemSize.height/2+SCROLL_BORDER; 
    CGFloat bottomScrollLimit = self.collectionView.contentOffset.y+self.collectionView.frame.size.height-layout.itemSize.height/2-SCROLL_BORDER; 
    CGPoint contentOffset = self.collectionView.contentOffset; 

    if (gesturePoint.y < topScrollLimit && gesturePoint.y - layout.itemSize.height/2 - SCROLL_BORDER > 0) 
     contentOffset.y -= SCROLL_STEP; 
    else if (gesturePoint.y > bottomScrollLimit && 
      gesturePoint.y + layout.itemSize.height/2 + SCROLL_BORDER < self.collectionView.contentSize.height) 
     contentOffset.y += SCROLL_STEP; 

    [self.collectionView setContentOffset:contentOffset]; 
} 

回答

69

这可能有助于

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

这扩展了UICollectionView以允许每个UICollectionViewCells可以通过长时间触摸(又名触摸并按住)手动重新排列。用户可以将单元格拖动到集合中的任何其他位置,其他单元格将自动重新排序。非常感谢lxcid

+18

发布答案:

private var longPressGesture: UILongPressGestureRecognizer! override func viewDidLoad() { super.viewDidLoad() longPressGesture = UILongPressGestureRecognizer(target: self, action: "handleLongGesture:") self.collectionView.addGestureRecognizer(longPressGesture) } func handleLongGesture(gesture: UILongPressGestureRecognizer) { switch(gesture.state) { case UIGestureRecognizerState.Began: guard let selectedIndexPath = self.collectionView.indexPathForItemAtPoint(gesture.locationInView(self.collectionView)) else { break } collectionView.beginInteractiveMovementForItemAtIndexPath(selectedIndexPath) case UIGestureRecognizerState.Changed: collectionView.updateInteractiveMovementTargetPosition(gesture.locationInView(gesture.view!)) case UIGestureRecognizerState.Ended: collectionView.endInteractiveMovement() default: collectionView.cancelInteractiveMovement() } } 

来源。总结帖子的内容将大大改善这个答案。 – David

+0

谢谢你的回答,那就是我正在寻找的。他用计时器管理卷轴,但他以我更聪明的方式进行。它给了我一个计时器可以实现的技巧的证明,我将再次看看该解决方案,再次感谢。该代码很安静,并且不尊重Apple的CVC标准,但他的滚动解决方案很有趣。谢谢! – foOg

+0

非常简单,谢谢。唯一的一点是,在拖拽项目时,其背景会变黑。其原始背景清晰(透明)。 将尝试修复它,并报告回来。 – ancajic

47

Here是替代:

DraggableCollectionView和LXReorderableCollectionViewFlowLayout之间的差异是:

  • 数据源只更换一次。这意味着当用户拖动一个项目时,单元格被重新定位而不修改数据源。
  • 它的写法使得可以使用自定义布局。
  • 它使用CADisplayLink进行平滑滚动和动画。
  • 动画在拖动时被取消的频率较低。感觉更“自然”。
  • 该协议使用类似于UITableViewDataSource的方法扩展UICollectionViewDataSource

这是一项正在进行的工作。现在支持多个部分。

要使用自定义布局中使用它看DraggableCollectionViewFlowLayout。大部分逻辑存在于LSCollectionViewLayoutHelper中。 CircleLayoutDemo中还有一个例子,展示了如何从WWDC 2012工作中制作Apple的CircleLayout示例。

+0

DraggableCollectionView对我来说看起来很完美,但我看不出它是如何在Demo中实现的(即未设置委托和数据源),还有其他例子吗? –

+0

想象出来,需要将集合视图流设置为自定义类DraggableCollectionViewFlow –

+0

如果您想要添加可拖动功能的基本功能,这非常简单。 – Hayro

2

Here是另一种方法:

主要区别是,这种解决方案并不需要一个“鬼”或“虚拟”小区提供拖放功能。它只是使用单元本身。动画符合UITableView。它通过在移动时调整集合视图布局的私有数据源来工作。一旦你放手,它会告诉你的控制器你可以将改变提交给你自己的数据源。

我相信这对于大多数使用情况来说更简单一些。仍然是一项正在进行的工作,但还有另一种方法来实现这一点。大多数人应该觉得这很容易融入他们自己定制的UICollectionViewLayouts中。

3

如果你想试验自己的实验,我只写了一个基于Swift的教程,你可以看看。我试图建立最基本的案例,以便更容易地遵循this

30

从iOS 9开始,UICollectionView现在支持重新排序。

对于UICollectionViewController S,只是覆盖collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

UICollectionView S,你必须自己处理手势除执行上述UICollectionViewDataSource方法。

下面是从源代码:这无非是一个链接气馁 https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionView_class/#//apple_ref/doc/uid/TP40012177-CH1-SW67

http://nshint.io/blog/2015/07/16/uicollectionviews-now-have-easy-reordering/

+2

我无法使其工作,是否有必须更改的设置或其他任何设置? –

+0

这对我也不适用。 – crypt

+0

好像你必须重写'UICollectionViewDataSource'方法'collectionView:moveItemAt:to'才能使它工作。 – newDeveloper