60

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

让我们调用一个UItableView TV和一个UICollectionView CV(澄清以下解释)

我基本上是在尝试重现电视的拖放,但我没有使用编辑模式,只要触发长按手势,单元格就可以移动。它工作得很好,我正在使用 CV 的移动方法,一切都很好。

我更新了 CV 的 contentOffset 属性以在用户拖动单元格时处理滚动。当用户转到顶部和底部的特定矩形时,我会更新 contentOffset 和 CV 滚动。问题是当用户停止移动手指时,手势不会发送任何更新,这会使滚动停止并在用户移动手指后立即重新开始。

这种行为绝对不自然,我宁愿继续滚动,直到用户释放 CV,就像电视中的情况一样。电视拖放体验很棒,我真的很想重现同样的感觉。有谁知道他们在重新排序期间如何管理电视中的滚动?

  • 我尝试使用计时器重复触发滚动动作,只要手势位置在正确的位置,滚动很糟糕而且效率不高(非常缓慢和跳跃)。
  • 我还尝试使用 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];
}
4

5 回答 5

70

这可能会有所帮助

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

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

于 2012-10-06T07:29:59.303 回答
47

是一个替代方案:

DraggableCollectionView 和LXReorderableCollectionViewFlowLayout的区别是:

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

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

要将其与自定义布局一起使用,请参阅DraggableCollectionViewFlowLayout. 大多数逻辑存在于LSCollectionViewLayoutHelper. CircleLayoutDemo 中还有一个示例,展示了如何使 WWDC 2012 中的 Apple 的 CircleLayout 示例正常工作。

于 2013-05-28T05:38:21.127 回答
31

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

对于UICollectionViewControllers,只需覆盖collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

对于s,除了实现上述方法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/

于 2016-01-02T04:12:42.243 回答
3

如果你想尝试推出自己的,我刚刚写了一个基于 Swift 的教程,你可以看看。我尝试构建最基本的案例,以便更容易理解这一点

于 2015-03-31T16:07:11.533 回答
2

是另一种方法:

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

我相信对于大多数用例来说,使用它会更简单一些。仍在进行中的工作,但还有另一种方式来实现这一点。大多数人应该会发现这很容易合并到他们自己的自定义 UICollectionViewLayouts 中。

于 2014-11-10T00:17:13.417 回答