UICollectionView有效的拖放

我目前正在尝试使用UICollectionView实现UITableView重sorting行为。

我们来调用一个UItableView TV和一个UICollectionView CV (澄清下面的解释)

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

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

这种行为是绝对不自然的,我宁愿继续滚动,直到用户释放简历,因为它是在电视的情况下。 电视拖拽的经验是真棒,我真的想重现相同的感觉。 有谁知道他们如何pipe理在重新sorting电视的滚动?

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

我没有想到这个,所以如果有人有答案,我会嫁给他!

这里是longPress方法的实现:

- (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]; } 

这可能有帮助

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

这扩展了UICollectionView以允许每个UICollectionViewCells通过长时间触摸(又名触摸并保持)由用户手动重新安排。 用户可以将单元格拖到集合中的任何其他位置,其他单元格将自动重新sorting。 谢谢你去lxcid 。

这是一个替代scheme:

DraggableCollectionView和LXReorderableCollectionViewFlowLayout之间的区别是:

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

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

要在自定义布局中使用它,请参阅DraggableCollectionViewFlowLayout 。 大多数逻辑存在于LSCollectionViewLayoutHelper 。 CircleLayoutDemo中还有一个例子,展示了如何从WWDC 2012工作中创buildApple的CircleLayout示例。

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

对于UICollectionViewController ,只需重写collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

对于UICollectionView ,除了实现上面的UICollectionViewDataSource方法外,还必须自己处理手势。

以下是源代码:

 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() } } 

来源: 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/

如果你想实验自己,我只写了一个基于Swift的教程,你可以看看。 我试图build立最基本的情况,以便更容易遵循这一点 。

这是另一种方法:

关键的区别在于,该解决scheme不需要“鬼”或“虚拟”单元来提供拖放function。 它只是使用单元格本身。 animation符合UITableView。 它通过在移动时调整集合视图布局的私有数据源来工作。 一旦你放手,它会告诉你的控制器,你可以将改变提交给你自己的数据源。

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