Comment détecter que l'animation est terminée sur UITableView beginUpdates / endUpdates?


116

J'insère / supprime une cellule de tableau en utilisant insertRowsAtIndexPaths/deleteRowsAtIndexPathswrapped in beginUpdates/endUpdates. J'utilise également beginUpdates/endUpdateslors du réglage de rowHeight. Toutes ces opérations sont animées par défaut.

Comment puis-je détecter que l'animation est terminée lors de l'utilisation beginUpdates/endUpdates?


1
FYI: Cela s'applique -scrollToRowAtIndexPath:atScrollPosition:animated:également.
Ben Lachman

Réponses:


292

Et ça?

[CATransaction begin];

[CATransaction setCompletionBlock:^{
    // animation has finished
}];

[tableView beginUpdates];
// do some work
[tableView endUpdates];

[CATransaction commit];

Cela fonctionne car les animations tableView utilisent des CALayeranimations en interne. Autrement dit, ils ajoutent les animations à toute ouverture CATransaction. S'il n'y a pas d'ouverture CATransaction(le cas normal), alors une est implicitement commencée, qui se termine à la fin de la boucle d'exécution actuelle. Mais si vous en commencez un vous-même, comme c'est le cas ici, il utilisera celui-là.


7
Cela n'a pas fonctionné pour moi. Le bloc d'achèvement est appelé pendant que l'animation est toujours en cours d'exécution.
mrvincenzo

2
@MrVincenzo Avez-vous défini l' completionBlockavant beginUpdateset endUpdatescomme dans l'extrait de code?
Rudolf Adamkovič

2
[CATransaction commit]devrait être appelé après pas avant [tableView endUpdates].
Rudolf Adamkovič

6
Le bloc de complétion n'est jamais appelé si une cellule contient une vue avec des animations, c'est-à-dire UIActivityIndicatorView.
markturnip

3
Après tout ce temps, je n'ai jamais su cela. Or pur !! Rien de pire que de voir une belle animation se faire tuer avec le mal nécessaire de tableView.reloadData ().
DogCoffee

31

Version Swift


CATransaction.begin()

CATransaction.setCompletionBlock({
    do.something()
})

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()

6

Si vous ciblez iOS 11 et supérieur, vous devez utiliser à la UITableView.performBatchUpdates(_:completion:)place:

tableView.performBatchUpdates({
    // delete some cells
    // insert some cells
}, completion: { finished in
    // animation complete
})

5

Une solution possible pourrait être d'hériter de l'UITableView sur lequel vous appelez endUpdateset de l'écraser setContentSizeMethod, car UITableView ajuste sa taille de contenu pour correspondre aux lignes ajoutées ou supprimées. Cette approche devrait également fonctionner reloadData.

Pour s'assurer qu'une notification n'est envoyée qu'après avoir endUpdatesété appelée, on peut également écraser endUpdateset définir un indicateur à cet endroit.

// somewhere in header
@private BOOL endUpdatesWasCalled_;

-------------------

// in implementation file

- (void)endUpdates {
    [super endUpdates];
    endUpdatesWasCalled_ = YES;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];

    if (endUpdatesWasCalled_) {
        [self notifyEndUpdatesFinished];
        endUpdatesWasCalled_ = NO;
    }
}

5

Vous pouvez inclure vos opérations dans le bloc d'animation UIView comme suit:

- (void)tableView:(UITableView *)tableView performOperation:(void(^)())operation completion:(void(^)(BOOL finished))completion
{
    [UIView animateWithDuration:0.0 animations:^{

        [tableView beginUpdates];
        if (operation)
            operation();
        [tableView endUpdates];

    } completion:^(BOOL finished) {

        if (completion)
            completion(finished);
    }];
}

Crédits à https://stackoverflow.com/a/12905114/634940 .


4
Ce code n'a pas fonctionné pour moi. Le bloc d'achèvement a été appelé après endUpdates, mais avant la fin des animations.
Tim Camber

1
Cela ne marche pas. L'animation tableview a cessé de fonctionner lors de l'application de cet exemple.
Primoz990

Essayez de prolonger la durée (comme 0,3 sec) - cela devrait fonctionner (cela a fonctionné pour moi)
emem

1

Je n'ai pas encore trouvé de bonne solution (à court de sous-classer UITableView). J'ai décidé de l'utiliser performSelector:withObject:afterDelay:pour le moment. Pas idéal, mais fait le travail.

MISE À JOUR : Il semble que je puisse utiliser scrollViewDidEndScrollingAnimation:à cette fin (c'est spécifique à mon implémentation, voir commentaire).


1
scrollViewDidEndScrollingAnimationest appelé uniquement en réponse à setContentOffsetetscrollRectToVisible
samvermette

@samvermette Eh bien, dans mon cas, lorsque beginUpdates / endUpdates sont appelés, l'UITableView anime toujours le défilement. Mais cela est spécifique à ma mise en œuvre, donc je suis d'accord que ce n'est pas la meilleure réponse, mais cela fonctionne pour moi.
pixelfreak

Il semble qu'il est possible d'inclure les mises à jour dans un bloc d'animation UIView. Voir ma réponse ci-dessous: stackoverflow.com/a/14305039/634940 .
Zdenek

0

Vous pouvez utiliser tableView:willDisplayCell:forRowAtIndexPath:comme:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"tableView willDisplay Cell");
    cell.backgroundColor = [UIColor colorWithWhite:((indexPath.row % 2) ? 0.25 : 0) alpha:0.70];
}

Mais cela sera également appelé lorsqu'une cellule qui est déjà dans le tableau passe de l'écran hors de l'écran à l'écran, de sorte que ce n'est peut-être pas exactement ce que vous recherchez. Je viens de regarder toutes les méthodes UITableViewet UIScrollViewdéléguer et il ne semble pas y avoir quoi que ce soit à gérer juste après l'insertion d'une cellule.


Pourquoi ne pas simplement appeler la méthode que vous souhaitez appeler lorsque l'animation se termine après le endUpdates?

- (void)setDownloadedImage:(NSMutableDictionary *)d {
    NSIndexPath *indexPath = (NSIndexPath *)[d objectForKey:@"IndexPath"];
    [indexPathDelayed addObject:indexPath];
    if (!([table isDragging] || [table isDecelerating])) {
        [table beginUpdates];
        [table insertRowsAtIndexPaths:indexPathDelayed withRowAnimation:UITableViewRowAnimationFade];
        [table endUpdates];
        // --> Call Method Here <--
        loadingView.hidden = YES;
        [indexPathDelayed removeAllObjects];
    }
}

Merci, chown. Ne pensez pas que willDisplayCellcela fonctionnera. J'ai besoin que cela se produise après l'animation car je n'ai besoin de faire une mise à jour visuelle qu'une fois que tout est réglé.
pixelfreak

np @pixelfreak, si je pense à une autre façon de faire cela, je vous le ferai savoir.
chown
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.