web-dev-qa-db-ja.com

UICollectionViewの効果的なドラッグアンドドロップ

現在、UICollectionViewを使用してUITableViewの並べ替え動作を実装しようとしています。

ItableView TVICollectionView CVを呼び出しましょう(以下の説明を明確にするため)

私は基本的にテレビのドラッグ&ドロップを再現しようとしていますが、編集モードを使用していません。長押しジェスチャーがトリガーされるとすぐにセルを移動する準備ができています。それは完璧に動作します、私はCVの移動方法を使用しています、すべてがうまくいきます。

CVのcontentOffsetプロパティを更新して、ユーザーがセルをドラッグしているときにスクロールを処理します。ユーザーが上下の特定の四角形に移動すると、contentOffsetとCVスクロールを更新します。問題は、ユーザーが指の動きを止めると、ジェスチャーが更新を送信しないため、ユーザーが指を動かすとすぐにスクロールが停止して再開することです。

この動作は間違いなく自然なことではありません。テレビの場合のように、ユーザーがCVを放すまでスクロールし続けることをお勧めします。 TVのドラッグアンドドロップ操作はすばらしく、同じ感覚を再現したいです。再注文中にテレビのスクロールを管理する方法を知っている人はいますか?

  • ジェスチャー位置が適切な場所にあり、スクロールがひどく生産性が低い(非常に遅くてびくびく)限り、タイマーを使用してスクロールアクションを繰り返しトリガーしようとしました。
  • また、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];
}
59
foOg

これは役立つかもしれません

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

これにより、UICollectionViewが拡張され、UICollectionViewCellsのそれぞれを、ユーザーが長いタッチ(別名、タッチアンドホールド)で手動で再配置できるようになります。ユーザーはセルをコレクション内の他の位置にドラッグでき、他のセルは自動的に並べ替えられます。 lxcid に感謝します。

70
dantes85

ここ は代替です:

DraggableCollectionViewと LXReorderableCollectionViewFlowLayout の違いは次のとおりです。

  • データソースは一度だけ変更されます。これは、ユーザーがアイテムをドラッグしている間、データソースを変更せずにセルが再配置されることを意味します。
  • カスタムレイアウトで使用できるように書かれています。
  • CADisplayLinkを使用して、スムーズなスクロールとアニメーションを実現します。
  • アニメーションは、ドラッグ中にあまり頻繁にキャンセルされません。もっと「自然」に感じます。
  • このプロトコルは、UICollectionViewDataSourceと同様のメソッドでUITableViewDataSourceを拡張します。

それは進行中の作業です。複数のセクションがサポートされるようになりました。

カスタムレイアウトで使用するには、DraggableCollectionViewFlowLayoutを参照してください。ほとんどのロジックはLSCollectionViewLayoutHelperに存在します。 CircleLayoutDemoには、WWDC 2012のAppleのCircleLayoutの例を機能させる方法を示す例もあります。

47
Luke

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/

30
chrisamanse

独自のロールアウトを実験したい場合は、Swiftに基づいたチュートリアルを作成しました。簡単に理解できるように、最も基本的なケースを作成しようとしました this

3
Mike M

ここ は別のアプローチです:

主な違いは、このソリューションでは、ドラッグアンドドロップ機能を提供するために「ゴースト」または「ダミー」セルが必要ないことです。単にセル自体を使用します。アニメーションはUITableViewに沿っています。コレクションビューレイアウトのプライベートデータソースを調整しながら動作します。放すと、コントローラーに変更を自分のデータソースにコミットできることを伝えます。

ほとんどのユースケースで作業する方が少し簡単だと思います。まだ進行中の作業ですが、これを達成するためのさらに別の方法です。ほとんどの場合、これを独自のカスタムUICollectionViewLayoutsに組み込むのは非常に簡単です。

2
Henry T Kirk