2012-10-03 20 views
59

Actualmente estoy tratando de implementar el comportamiento de reordenamiento UITableView utilizando UICollectionView.UICollectionView efectiva arrastrar y soltar

Vamos a llamar a un UITableView TV y una UICollectionView CV (para aclarar la explicación siguiente)

básicamente estoy tratando de reproducir el arrastrar y soltar & de la televisión, pero no estoy utilizando el modo de edición , la celda está lista para moverse tan pronto como se active el gesto de presión prolongada. Funciona perfectamente, estoy usando el método de movimiento del CV, todo está bien.

Actualizo la propiedad contentOffset del CV para manejar el desplazamiento cuando el usuario está arrastrando una celda. Cuando un usuario va a un rect concreto en la parte superior e inferior, actualizo contentOffset y CV scroll. El problema es cuando el usuario deja de mover su dedo, el gesto no envía ninguna actualización que hace que el desplazamiento se detenga y vuelva a comenzar tan pronto como el usuario mueva su dedo.

Este comportamiento definitivamente no es natural, prefiero continuar desplazándome hasta que el usuario libere la CV como es el caso en la TV. La experiencia de caída del arrastre de TV & es impresionante y realmente quiero reproducir la misma sensación. ¿Alguien sabe cómo manejan el rollo en la TV durante el reordenamiento?

  • He intentado utilizar un temporizador para activar una acción de desplazamiento varias veces, siempre y cuando la posición gesto está en el lugar correcto, el rollo era horrible y no muy productivo (muy lento y nervioso).
  • También traté de usar GCD para escuchar la posición del gesto en otro hilo, pero el resultado es aún peor.

Me quedé sin idea sobre eso, así que si alguien tiene la respuesta, ¡me casaría con él!

Aquí está la implementación del método 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];}]; 
    } 
} 

Para hacer el desplazamiento CV, estoy usando ese método:

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

Respuesta

69

Esto podría ayudar

https://github.com/lxcid/LXReorderableCollectionViewFlowLayout

Esto extiende el UICollectionView para permitir que cada uno de los UICollectionViewCells para ser reordenado manualmente por el usuario con un toque largo (también conocido como tocar y mantener). El usuario puede arrastrar la Celda a cualquier otra posición en la colección y las otras celdas se reordenarán automáticamente. Gracias por ir al lxcid.

+18

Publicación respuestas que no son más que un enlace no se recomienda. Resumir el contenido de la publicación mejoraría en gran medida esta respuesta. – David

+0

Gracias por la respuesta, eso es lo que estoy buscando. Él maneja el rollo con un temporizador pero lo hace de una manera más inteligente que yo. Me dio la prueba de que un temporizador podría hacer el truco, echaré un vistazo a esa solución, gracias de nuevo. El código es silencioso y no respeta los estándares de Apple para CVC, pero su solución para el desplazamiento es interesante. ¡Gracias! – foOg

+0

Muy simple, gracias. Lo único es que, al arrastrar un elemento, su fondo se vuelve negro. Su fondo original es claro (transparente). Trataré de arreglarlo e informaré. – ancajic

47

Here es una alternativa:

Las diferencias entre DraggableCollectionView y LXReorderableCollectionViewFlowLayout son:

  • La fuente de datos sólo se cambia una vez. Esto significa que mientras el usuario está arrastrando un elemento, las celdas se vuelven a colocar sin modificar la fuente de datos.
  • Está escrito de tal manera que hace posible su uso con diseños personalizados.
  • Utiliza un CADisplayLink para un desplazamiento suave y animación.
  • Las animaciones se cancelan con menos frecuencia mientras se arrastra. Se siente más "natural".
  • El protocolo se extiende UICollectionViewDataSource con métodos similares a UITableViewDataSource.

Es un trabajo en progreso. Múltiples secciones ahora son compatibles.

Para usarlo con un diseño personalizado, vea DraggableCollectionViewFlowLayout. La mayor parte de la lógica existe en LSCollectionViewLayoutHelper. También hay un ejemplo en CircleLayoutDemo que muestra cómo hacer que el ejemplo de CircleLayout de Apple de la WWDC 2012 funcione.

+0

El DraggableCollectionView parece perfecto para mí, pero no puedo ver cómo se implementa en la demostración (es decir, el delegado y el origen de datos no están configurados), ¿hay más ejemplos? –

+0

Lo encontré necesario para establecer el flujo de la Vista de recopilación en la clase personalizada DraggableCollectionViewFlow –

+0

Esto es perfecto y fácil si está buscando algo básico solo para agregar funcionalidad arrastrable. – Hayro

2

Here es otro enfoque:

diferencia clave es que esta solución no requiere un "fantasma" o célula "de prueba" para proporcionar la funcionalidad de arrastrar y soltar. Simplemente usa la celda misma. Las animaciones están en línea con UITableView. Funciona al ajustar el origen de datos privado del diseño de vista de colección mientras se mueve. Una vez que lo dejes ir, le dirá a tu controlador que puedes comprometer el cambio a tu propia fuente de datos.

Creo que es un poco más fácil de usar para la mayoría de los casos de uso. Todavía es un trabajo en progreso, pero es otra forma de lograr esto. La mayoría debería encontrar esto bastante fácil de incorporar a sus propios UICollectionViewLayouts personalizados.

3

Si desea experimentar su propio desarrollo, acabo de escribir un tutorial basado en Swift que puede consultar. Traté de construir el más básico de los casos para que sea más fácil seguir this.

30

A partir de iOS 9, UICollectionView ahora es compatible con la reordenación.

Para UICollectionViewController s, simplemente anular collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath)

Para UICollectionView s, usted tiene que manejar los gestos usted mismo, además de la realización del procedimiento UICollectionViewDataSource anteriormente.

Aquí está el código de la fuente:

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

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

+2

No pude hacer que esto funcione, ¿hay alguna configuración que deba cambiarse o alguna otra cosa? –

+0

Esto no funcionó para mí también. – crypt

+0

parece que tendrá que anular el método 'UICollectionViewDataSource'' collectionView: moveItemAt: to' para hacer que esto funcione. – newDeveloper