Comment être averti quand scrollToRowAtIndexPath finit d'animer

Ceci est une suite de Comment être averti quand un tableViewController finit d'animer le push sur une stack de nav.

Dans une tableView je veux désélectionner une ligne avec une animation, mais seulement après que la tableView ait fini d' animer le défilement jusqu'à la ligne sélectionnée. Comment puis-je être informé quand cela arrive, ou quelle méthode est appelée le moment qui se termine.

C'est l'ordre des choses:

  1. Contrôleur de vue Push
  2. Dans viewWillAppear je sélectionne une certaine ligne.
  3. Dans viewDidAppear I scrollToRowAtIndexPath (à la ligne sélectionnée).
  4. Ensuite, lorsque le défilement se termine, je souhaite deselectRowAtIndexPath: animated:YES

De cette façon, l'user saura pourquoi ils ont défilé là, mais alors je peux disparaître la sélection.
L'étape 4 est la partie que je n'ai pas encore comprise. Si je l'appelle dans viewDidAppear alors au moment où la tableView défile, la ligne a déjà été désélectionnée ce qui n'est pas bon.

Vous pouvez utiliser la méthode scrollViewDidEndScrollingAnimation: du délégué table view. En effet, UITableView est une sous-class de UIScrollView et UITableViewDelegate est conforme à UIScrollViewDelegate . En d'autres termes, une vue de table est une vue de défilement et un délégué de vue de table est également un délégué de vue de défilement.

Créez donc une méthode scrollViewDidEndScrollingAnimation: dans votre délégué de vue de table et déselect la cellule de cette méthode. Voir la documentation de reference pour UIScrollViewDelegate pour plus d'informations sur la méthode scrollViewDidEndScrollingAnimation:

essaye ça

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

N'oubliez pas de mettre en animation à NO, l'animation de scrollToRow sera surchargée par UIView animateWithDuration.

J'espère que cette aide!

Pour répondre au commentaire de Ben Packard sur la réponse acceptée, vous pouvez le faire. Testez si la tableView peut faire défiler jusqu'à la nouvelle position. Sinon, exécutez votre méthode immédiatement. S'il peut défiler, attendez que le défilement soit terminé pour exécuter votre méthode.

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

Vous pouvez inclure le scrollToRowAtIndexPath: dans un [UIView animateWithDuration:...] qui triggersra le bloc d'achèvement après la fin de toutes les animations incluses. Donc, quelque chose comme ça:

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

Implémenter ceci dans une extension 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) } } } 

Cela fonctionne dans la plupart des cas bien que le délégué TableView soit modifié pendant le défilement, ce qui peut être indésirable dans certains cas.