Подтвердить что ты не робот

Эффективное перетаскивание UICollectionView

В настоящее время я пытаюсь реализовать поведение переупорядочения UITableView с помощью UICollectionView.

Позвоните в UItableView TV и CV UICollectionView (чтобы пояснить следующее объяснение)

Я в основном пытаюсь воспроизвести drag & drop телевизора, но я не использую режим редактирования, ячейка готова к перемещению, как только будет активирован длительный жест нажатия. Он работает префектно, я использую метод перемещения CV, все в порядке.

Я обновляю свойство contentOffset CV для обработки прокрутки, когда пользователь перетаскивает ячейку. Когда пользователь переходит к конкретному прямоугольнику сверху и снизу, я обновляю contentOffset и CV scroll. Проблема в том, что пользователь перестает перемещать его пальцем, этот жест не отправляет обновления, которое заставляет прокрутку останавливаться и запускаться снова, как только пользователь перемещает палец.

Такое поведение, безусловно, не является естественным, я бы предпочел продолжить прокрутку, пока пользователь не выпустит 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];
}
4b9b3361

Ответ 1

Это может помочь

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

Это расширяет UICollectionView, чтобы позволить каждому из UICollectionViewCells вручную переключиться пользователем с длинным касанием (ака touch-and-hold). Пользователь может перетащить ячейку в любую другую позицию в коллекции, а остальные ячейки будут автоматически изменяться. Спасибо за это lxcid.

Ответ 2

Здесь является альтернативой:

Различия между DraggableCollectionView и LXReorderableCollectionViewFlowLayout:

  • Источник данных изменяется только один раз. Это означает, что, пока пользователь перетаскивает элемент, ячейки повторно помещаются без изменения источника данных.
  • Он написан таким образом, что позволяет использовать его с настраиваемыми макетами.
  • Он использует CADisplayLink для плавной прокрутки и анимации.
  • Анимация отменяется реже во время перетаскивания. Он чувствует себя более "естественным".
  • Протокол расширяет UICollectionViewDataSource с помощью методов, аналогичных UITableViewDataSource.

Это незавершенное производство. Теперь поддерживаются несколько разделов.

Чтобы использовать его с настраиваемым макетом, см. DraggableCollectionViewFlowLayout. Большая часть логики существует в LSCollectionViewLayoutHelper. В CircleLayoutDemo также есть пример, показывающий, как работать с примером Apple CircleLayout из WWDC 2012.

Ответ 3

Начиная с iOS 9, UICollectionView теперь поддерживает переупорядочение.

Для UICollectionViewController s просто переопределите collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

Для UICollectionView s вам придется самостоятельно обрабатывать жесты, кроме того, чтобы реализовать метод 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/

Ответ 4

Если вы хотите экспериментировать с развертыванием своих собственных, я просто написал учебник Swift, который вы можете посмотреть. Я попытался построить самые простые случаи, чтобы было легче следовать this.

Ответ 5

Здесь - другой подход:

Ключевое отличие заключается в том, что для этого решения не требуется ячейка "призрак" или "dummy", чтобы обеспечить функциональность перетаскивания. Он просто использует сама ячейку. Анимации соответствуют UITableView. Он работает, настраивая частный источник данных коллекции, перемещаясь. Как только вы отпустите, он сообщит вашему контроллеру, что вы можете зафиксировать это изменение на свой собственный источник данных.

Я считаю, что это немного проще для работы в большинстве случаев использования. Все еще идет работа, но это еще один способ добиться этого. Большинство из них должны найти это довольно легко включить в свои собственные UICollectionViewLayouts.