Cómo recibir una notificación cuando scrollToRowAtIndexPath termina de animar

Este es un seguimiento de cómo recibir una notificación cuando un tableViewController termina de animar el empuje en una stack de naves.

En un tableView quiero anular la selección de una fila con animation, pero solo después de que la tablaView haya terminado de animar el desplazamiento a la fila seleccionada. ¿Cómo puedo notificarme cuando eso sucede, o qué método se llama el momento en que termina?

Este es el order de las cosas:

  1. Controlador de vista de empuje
  2. En viewWillAppear selecciono una cierta fila.
  3. En viewDidAppear , viewDidAppear (a la fila seleccionada).
  4. Luego, cuando eso termine de desplazarse, quiero deselectRowAtIndexPath: animated:YES

De esta manera, el usuario sabrá por qué se desplazaron allí, pero luego puedo desvanecer la selección.
El paso 4 es la parte que aún no he descubierto. Si lo llamo en viewDidAppear entonces, en el momento en que el tableView se desplaza allí, la fila ya no está seleccionada, lo que no es bueno.

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

Por lo tanto, cree un método scrollViewDidEndScrollingAnimation: en su delegado de vista de tabla y anule la selección de la celda en ese método. Consulte la documentation de reference para UIScrollViewDelegate para get información sobre el método scrollViewDidEndScrollingAnimation:

testing esto

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

No olvide configurar animado en NO, la animation de scrollToRow será anulada por UIView animateWithDuration.

Espero que esto ayude !

Para abordar el comentario de Ben Packard sobre la respuesta aceptada, puede hacer esto. Pruebe si la tablaView puede desplazarse a la nueva position. De lo contrario, ejecute su 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 requinetworking 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]; } 

Puede include scrollToRowAtIndexPath: dentro de un [UIView animateWithDuration:...] que activará el bloque de finalización una vez que todas las animaciones incluidas concluyan. Por lo tanto, algo como esto:

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

Implementando esto en una extensión Swift.

 //strong ref requinetworking 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 el delegado de TableView se cambia durante el desplazamiento, lo que puede ser no deseado en algunos casos.