UICollectionView efficace glisser-déposer

J’essaie actuellement d’implémenter le comportement de réorganisation UITableView en utilisant UICollectionView.

Appelons un téléviseur UItableView et un CV UICollectionView (pour clarifier l’explication suivante)

J’essaie essentiellement de reproduire le glisser-déposer du téléviseur, mais je n’utilise pas le mode d’édition, la cellule est prête à être déplacée dès que le geste de pression longue est déclenché. Ca marche parfaitement, j’utilise la méthode move du CV, tout va bien.

Je mets à jour la propriété contentOffset du CV pour gérer le défilement lorsque l’utilisateur fait glisser une cellule. Lorsqu’un utilisateur accède à un rectangle en haut et en bas, je mets à jour le contenu et le défilement du CV. Le problème est que lorsque l’utilisateur arrête de déplacer son doigt, le geste n’envoie aucune mise à jour qui fait que le défilement s’arrête et redémarre dès que l’utilisateur déplace son doigt.

Ce comportement n’est certainement pas naturel, je préférerais continuer à défiler jusqu’à ce que l’utilisateur libère le CV comme c’est le cas sur le téléviseur. L’expérience drag & drop TV est géniale et je veux vraiment reproduire le même sentiment. Est-ce que quelqu’un sait comment ils gèrent le défilement dans la télévision pendant la réorganisation?

  • J’ai essayé d’utiliser une timer pour déclencher une action de défilement à plusieurs resockets tant que la position du geste est au bon endroit, le défilement était horrible et pas très productif (très lent et nerveux).
  • J’ai également essayé d’utiliser GCD pour écouter la position du geste dans un autre thread mais le résultat est encore pire.

Je n’ai pas eu d’idée à ce sujet, alors si quelqu’un a la réponse, je l’épouserais!

Voici l’implémentation de la méthode 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];}]; } } 

Pour faire défiler le CV, j’utilise cette méthode:

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

Cela pourrait aider

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

Ceci étend le UICollectionView pour permettre à l’ UICollectionViewCells de réorganiser manuellement chaque UICollectionViewCells avec une touche longue (aka Touch and Hold). L’utilisateur peut faire glisser la cellule vers n’importe quelle autre position de la collection et les autres cellules seront réorganisées automatiquement. Merci à Lxcid pour cela.

Voici une alternative:

Les différences entre DraggableCollectionView et LXReorderableCollectionViewFlowLayout sont les suivantes:

  • La source de données n’est modifiée qu’une fois. Cela signifie que lorsque l’utilisateur fait glisser un élément, les cellules sont repositionnées sans modifier la source de données.
  • Il est écrit de telle manière qu’il est possible d’utiliser des mises en page personnalisées.
  • Il utilise un CADisplayLink pour le défilement et l’animation en douceur.
  • Les animations sont annulées moins fréquemment lors du déplacement. Il se sent plus “naturel”.
  • Le protocole étend UICollectionViewDataSource avec des méthodes similaires à UITableViewDataSource .

C’est un travail en cours. Plusieurs sections sont maintenant sockets en charge.

Pour l’utiliser avec une présentation personnalisée, voir DraggableCollectionViewFlowLayout . La plupart de la logique existe dans LSCollectionViewLayoutHelper . Il y a aussi un exemple dans CircleLayoutDemo montrant comment créer l’exemple CircleLayout d’Apple à partir du travail de WWDC 2012.

Depuis iOS 9, UICollectionView prend désormais en charge la réorganisation.

Pour UICollectionViewController s, remplacez simplement collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

Pour UICollectionView s, vous devrez gérer les gestes vous-même en plus d’implémenter la méthode UICollectionViewDataSource ci-dessus.

Voici le code de la source:

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

Sources: 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/

Si vous voulez expérimenter le vôtre, je viens juste d’écrire un tutoriel basé sur Swift que vous pouvez regarder. J’ai essayé de construire les cas les plus élémentaires pour être plus facile à suivre.

Voici une autre approche:

La principale différence est que cette solution ne nécessite pas de cellule “fantôme” ou “factice” pour fournir la fonctionnalité de glisser-déposer. Il utilise simplement la cellule elle-même. Les animations sont conformes à UITableView. Cela fonctionne en ajustant la source de données privée de la disposition de la vue de collection tout en se déplaçant. Une fois que vous lâchez, il indiquera à votre contrôleur que vous pouvez valider le changement sur votre propre source de données.

Je pense que c’est un peu plus simple de travailler avec la plupart des cas d’utilisation. Encore un travail en cours, mais encore une autre façon d’y parvenir. La plupart devraient trouver cela assez facile à incorporer dans leurs propres UICollectionViewLayouts.