web-dev-qa-db-fra.com

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

J'insère/supprime une cellule de tableau à l'aide de insertRowsAtIndexPaths/deleteRowsAtIndexPaths enveloppé dans beginUpdates/endUpdates J'utilise aussi beginUpdates/endUpdates lors du réglage de rowHeight. Toutes ces opérations sont animées par défaut.

Comment puis-je détecter que l'animation s'est terminée avec beginUpdates/endUpdates?

107
pixelfreak

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 animations CALayer en interne. C'est-à-dire qu'ils ajoutent les animations à toute CATransaction ouverte. S'il n'y a pas de CATransaction ouvert (le cas normal), alors l'une d'entre elles est commencée implicitement et se termine à la fin du cycle d'exécution en cours. Mais si vous en commencez un vous-même, comme c'est le cas ici, alors il l'utilisera. 

284
Rudolf Adamkovič

Version rapide


CATransaction.begin()

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

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
27
Michael

Vous pouvez inclure vos opérations dans le bloc d’animation UIView de la manière suivante:

- (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 .

5
Zdenek

Une solution possible pourrait être d'hériter de UITableView sur lequel vous appelez endUpdates et d'écraser sa setContentSizeMethod, puisque UITableView ajuste la taille de son contenu pour qu'elle corresponde aux lignes ajoutées ou supprimées. Cette approche devrait également fonctionner pour reloadData.

Pour s'assurer qu'une notification est envoyée uniquement après l'appel de endUpdates, vous pouvez également écraser endUpdates et y définir un indicateur.

// 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;
    }
}
4
Antoni

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

UPDATE: Il semble que je puisse utiliser scrollViewDidEndScrollingAnimation: à cette fin (cela est spécifique à mon implémentation, voir le commentaire).

0
pixelfreak

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 déjà présente dans la table se déplacera de l'écran à l'écran, de sorte que ce ne sera peut-être pas exactement ce que vous recherchez. Je viens de parcourir toutes les méthodes de délégué UITableView et UIScrollView et il ne semble pas y avoir quoi que ce soit à gérer juste après l'animation d'une cellule insérée.


Pourquoi ne pas simplement appeler la méthode que vous voulez appeler lorsque l'animation se termine après la 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];
    }
}
0
chown

Si vous ciblez iOS 11 ou une version ultérieure, vous devez utiliser UITableView.performBatchUpdates(_:completion:) à la place:

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