2010-12-04 10 views
30

Este es un seguimiento de How to get notified when a tableViewController finishes animating the push onto a nav stack.Cómo ser notificado cuando scrollToRowAtIndexPath termina la animación

En un tableView Quiero anular la selección de una fila con la animación, pero sólo después de que el tableView tiene terminado la animación de desplazamiento para el seleccionado fila. ¿Cómo puedo recibir una notificación cuando eso sucede, o qué método se llama en el momento en que termina?

Este es el orden de las cosas:

  1. controlador de vista de empuje
  2. En viewWillAppear selecciono una cierta fila.
  3. En viewDidAppear I scrollToRowAtIndexPath (a la fila seleccionada).
  4. Luego, cuando termina el desplazamiento que quiero deselectRowAtIndexPath: animated:YES

De esta manera, el usuario sabrá qué fueron desplazan allí, pero luego puede desvanecer la selección.
El paso 4 es la parte que aún no he descifrado. Si lo llamo en viewDidAppear, cuando TableView se desplace allí, la fila ya no está seleccionada, lo cual no es bueno.

Respuesta

56

Puede usar el método scrollViewDidEndScrollingAnimation: del delegado de la vista de tabla. Esto se debe a que UITableView es una subclase de UIScrollView y UITableViewDelegate conforme a UIScrollViewDelegate. En otras palabras, una vista de tabla es una vista de desplazamiento, y un delegado de vista de tabla es también un delegado de vista de desplazamiento.

Por lo tanto, cree un método scrollViewDidEndScrollingAnimation: en la vista de tabla delegue y anule la selección de la celda en ese método. Consulte la documentación de referencia para UIScrollViewDelegate para obtener información sobre el método scrollViewDidEndScrollingAnimation:.

+0

¡Gracias de nuevo! – Andrew

+0

De nada. –

+41

Sería bueno si iOS proporciona una versión con bloque de finalización (como 'UIView animateWithDuration: animations: completion'), por lo que la notificación puede ser específica del contexto ... – pixelfreak

11

Para abordar el comentario de Ben Packard sobre la respuesta aceptada, puede hacerlo. Pruebe si tableView puede desplazarse a la nueva posición. Si no, ejecuta tu método de inmediato. Si puede desplazarse, espere hasta que el desplazamiento finalice para ejecutar su método.

- (void)someMethod 
{ 
    CGFloat originalOffset = self.tableView.contentOffset.y; 
    [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:NO]; 
    CGFloat offset = self.tableView.contentOffset.y; 

    if (originalOffset == offset) 
    { 
     // scroll animation not required because it's already scrolled exactly there 
     [self doThingAfterAnimation]; 
    } 
    else 
    { 
     // We know it will scroll to a new position 
     // Return to originalOffset. animated:NO is important 
     [self.tableView setContentOffset:CGPointMake(0, originalOffset) animated:NO]; 
     // Do the scroll with animation so `scrollViewDidEndScrollingAnimation:` will execute 
     [self.tableView scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; 
    } 
} 

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 
{ 
    [self doThingAfterAnimation]; 
} 
+1

¿Qué le parece usar '[self.tableView.indexPathsForVisibleRows containsObject: path]'? – Pim

+2

Es cierto que si indexPath * no está * visible, definitivamente tendrá que desplazarse para colocarse correctamente, por lo que podría omitir el toque 'contentOffset' en ese caso. Sin embargo, si indexPath ** está ** visible, el tableView puede o no ser capaz de desplazarlo a su posición, por lo que debe verificar los contentOffsets (los mismos que se muestran arriba). – EthanB

8

Puede incluir la scrollToRowAtIndexPath: dentro de un bloque [UIView animateWithDuration:...] que activará el bloque de finalización después de todo incluido animaciones concluyen. Por lo tanto, algo como esto:

[UIView 
    animateWithDuration:0.3f 
    delay:0.0f 
    options:UIViewAnimationOptionAllowUserInteraction 
    animations:^ 
    { 
     // Scroll to row with animation 
     [self.tableView scrollToRowAtIndexPath:indexPath 
          atScrollPosition:UITableViewScrollPositionTop 
            animated:YES]; 
    } 
    completion:^(BOOL finished) 
    { 
     // Deselect row 
     [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 
    }]; 
+0

Inteligente :) Aunque eso anula la duración predeterminada de la animación para scrollToRowAtIndexPath? – BastiBen

+0

correcto, anula la duración predeterminada, pero dependiendo del valor que establezca, podría hacer que su aplicación actúe de forma distinta/exclusiva (lo que podría ser una buena opción) o podría encontrar un valor tan cercano al valor predeterminado que se convierta en casi indiscernible para tus usuarios. –

+0

Buena respuesta, pero el parámetro 'animado' de' scrollToRowAtIndexPath' debe establecerse en NO: ya está dentro del bloque de animación. –

28

prueba este

[UIView animateWithDuration:0.3 animations:^{ 
    [yourTableView scrollToRowAtIndexPath:indexPath 
         atScrollPosition:UITableViewScrollPositionTop 
           animated:NO]; 
} completion:^(BOOL finished){ 
    //do something 
}]; 

No se olvide de establecer animada en NO, la animación de scrollToRow será anulado por UIView animateWithDuration.

¡Espero que esta ayuda!

+1

Pude hacer lo que quería basándome en esta sugerencia, así que supongo que soy dueño de uno :) – alasker

+0

CUIDADO CON EL USO DE ESTA SOLUCIÓN: Estaba usando este método por un tiempo, hasta que vi un comportamiento extraño en las celdas que se desplazaban a . Las imágenes que estaban ocultas se mostrarían ... inexplicables. Ver la depuración mostraría que no eran visibles. Pero fueron cuando se ejecutan en un dispositivo. Aislé el problema al scrollToRowAtIndexPath con el conjunto animado en NO dentro del bloque de animación. Muévelo fuera del bloque de animación y no hay problema. Pero luego no hay animación ... Entonces esta técnica puede causar problemas. Actualicé para usar la solución scrollViewDidEndScrollingAnimation de James Huddleston para solucionar el problema. –

0

Implementando esto en una extensión Swift.

//strong ref required 
private var lastDelegate : UITableViewScrollCompletionDelegate! = nil 

private class UITableViewScrollCompletionDelegate : NSObject, UITableViewDelegate { 
    let completion:() ->() 
    let oldDelegate : UITableViewDelegate? 
    let targetOffset: CGPoint 
    @objc private func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) { 
     scrollView.delegate = oldDelegate 
     completion() 
     lastDelegate = nil 
    } 

    init(completion:() ->(), oldDelegate: UITableViewDelegate?, targetOffset: CGPoint) { 
     self.completion = completion 
     self.oldDelegate = oldDelegate 
     self.targetOffset = targetOffset 
     super.init() 
     lastDelegate = self 
    } 
} 

extension UITableView { 
    func scrollToRowAtIndexPath(indexPath: NSIndexPath, atScrollPosition scrollPosition: UITableViewScrollPosition, animated: Bool, completion:() ->()) { 
     assert(lastDelegate == nil, "You're already scrolling. Wait for the last completion before doing another one.") 
     let originalOffset = self.contentOffset 
     self.scrollToRowAtIndexPath(indexPath, atScrollPosition: scrollPosition, animated: false) 

     if originalOffset.y == self.contentOffset.y { //already at the right position 
      completion() 
      return 
     } 
     else { 
      let targetOffset = self.contentOffset 
      self.setContentOffset(originalOffset, animated: false) 
      self.delegate = UITableViewScrollCompletionDelegate(completion: completion, oldDelegate: self.delegate, targetOffset:targetOffset) 
      self.scrollToRowAtIndexPath(indexPath, atScrollPosition: scrollPosition, animated: true) 
     } 
    } 
} 

Esto funciona para la mayoría de los casos, aunque se cambia el delegado TableView durante el desplazamiento, lo que puede ser indeseable en algunos casos.

Cuestiones relacionadas