【问题标题】:UICollectionView effective drag and dropUICollectionView 有效拖拽
【发布时间】:2012-09-24 15:41:14
【问题描述】:

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

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

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

我更新了 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];}];
    }
}

为了让简历滚动,我使用的是那个方法:

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

【问题讨论】:

    标签: scroll uiscrollview uicollectionview drag-and-drop


    【解决方案1】:

    这可能会有所帮助

    https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

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

    【讨论】:

    • 不鼓励发布仅仅是链接的答案。总结帖子的内容将大大改善这个答案。
    • 谢谢你的回答,这就是我要找的。他用计时器管理卷轴,但他以一种比我更聪明的方式来做这件事。它给了我一个计时器可以解决问题的证据,我会看看那个解决方案,再次感谢。代码非常混乱,不尊重 Apple 的 CVC 标准,但他的滚动解决方案很有趣。谢谢!
    • 非常简单,谢谢。唯一的问题是,在拖动项目时,其背景变为黑色。其原始背景清晰(透明)。将尝试修复它,并报告。
    • 已修复。在“LX_rasterizedImage”方法中找到以下行并将 NO 代替注释段: UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO/*self.isOpaque*/, 0.0f);
    • 单元格大小不同时如何拖动单元格?
    【解决方案2】:

    Here 是另一种选择:

    DraggableCollectionView 和LXReorderableCollectionViewFlowLayout 的区别是:

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

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

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

    【讨论】:

    • DraggableCollectionView 对我来说似乎很完美,但我看不到它是如何在 Demo 中实现的(即未设置 Delegate 和 Datasource),还有更多示例吗?
    • 想通了,需要将 Collection View 流设置为自定义类 DraggableCollectionViewFlow
    • 如果您正在寻找一些基本的东西来添加可拖动功能,这是完美而简单的。
    • 它运行良好,尽管您需要保持固定的单元格大小。 (而且我还在研究如何用它做一个水平的 UICollectionView)
    • @PeterJohnson 这是在计算后进行的不幸副作用。我有一个实验性的重写来解决这个问题。由于时间不够,我已经推动了我到目前为止的工作,我欢迎任何帮助。见github.com/lukescott/DraggableCollectionView/issues/30
    【解决方案3】:

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

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

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

    【讨论】:

    • 我无法让它工作,是否有必须更改的设置或其他什么?
    • 看来您必须重写 UICollectionViewDataSource 方法 collectionView:moveItemAt:to 才能使其正常工作。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      Here 是另一种方法:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多