Durée de l'animation de ligne UITableView et rappel d'achèvement

98

Existe-t-il un moyen de spécifier la durée des animations de ligne UITableView ou d'obtenir un rappel lorsque l'animation se termine?

Ce que je voudrais faire, c'est faire clignoter les indicateurs de défilement une fois l'animation terminée. Faire le flash avant cela ne fait rien. Jusqu'à présent, la solution de contournement que j'ai est de retarder une demi-seconde (cela semble être la durée d'animation par défaut), c'est-à-dire:

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];
Daniel Dickison
la source
Je n'ai pas essayé moi-même, mais peut-être que cela pourrait le faire, avec une certaine gestion du chemin d'index:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle

Réponses:

3

De nos jours, si vous voulez faire cela, il y a une nouvelle fonction à partir d'iOS 11 :

- (void)performBatchUpdates:(void (^)(void))updates 
                 completion:(void (^)(BOOL finished))completion;

Dans les fermetures de mises à jour, vous placez le même code que dans la section beginUpdates () / endUpdates. Et la complétion est exécutée après toutes les animations.

Michał Ziobro
la source
C'est bien. Je n'avais pas remarqué cet ajout.
Daniel Dickison
207

Je viens de tomber sur ça. Voici comment procéder:

Objectif c

[CATransaction begin];
[tableView beginUpdates];
[CATransaction setCompletionBlock: ^{
    // Code to be executed upon completion
}];
[tableView insertRowsAtIndexPaths: indexPaths
                 withRowAnimation: UITableViewRowAnimationAutomatic];
[tableView endUpdates];
[CATransaction commit];

Rapide

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock {
    // Code to be executed upon completion
}
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()
karwag
la source
2
Encore une fois, fonctionne parfaitement ici. iOS6 et tout. Il s'agit d'un mécanisme approprié pris en charge par le SDK pour remplacer les propriétés dans les animations par défaut. Peut-être avez-vous des animations supplémentaires plus longues dans votre CATransaction? Ils s'emboîtent, vous savez.
karwag
1
Fonctionne très bien pour moi dans iOS6. Merci pour ça!
Aron
5
setAnimationDurationne semble pas affecter la durée d'insertion / suppression. iOS 6
Tom Redman
2
des suggestions sur la façon de changer la durée? CATransaction setAnimationDuration: ne semble pas faire de différence.
Jeff Grimes
5
Fonctionne bien pour moi aussi dans iOS 5.1.1, 6.1, 7.0; Mais, si vous avez besoin d'obtenir une nouvelle tableView.contentSize après l'animation (comme c'était le cas dans mon cas), vous devez utiliser [self performSelectorOnMainThread: withObject: waitUntilDone:]; dans setCompletionBlock afin d'appeler votre délégué au prochain runloop. si vous appelez directement votre délégué, sans performSelectorOnMainThread, vous obtenez l'ancienne valeur pour tableView.contentSize.
slamour le
38

En développant la bonne réponse de karwag , notez que sur iOS 7, entourer la CATransaction avec une animation UIView permet de contrôler la durée de l'animation de la table.

[UIView beginAnimations:@"myAnimationId" context:nil];

[UIView setAnimationDuration:10.0]; // Set duration here

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    NSLog(@"Complete!");
}];

[myTable beginUpdates];
// my table changes
[myTable endUpdates];

[CATransaction commit];
[UIView commitAnimations];

La durée de l'animation UIView n'a aucun effet sur iOS 6. Peut-être que les animations de table iOS 7 sont implémentées différemment, au niveau UIView.

Brent
la source
La durée de l'animation semble être ignorée.
Dustin
26

C'est un sacré truc utile! J'ai écrit une extension UITableView pour éviter d'écrire des trucs CATransaction tout le temps.

import UIKit

extension UITableView {

    /// Perform a series of method calls that insert, delete, or select rows and sections of the table view.
    /// This is equivalent to a beginUpdates() / endUpdates() sequence, 
    /// with a completion closure when the animation is finished.
    /// Parameter update: the update operation to perform on the tableView.
    /// Parameter completion: the completion closure to be executed when the animation is completed.

    func performUpdate(_ update: ()->Void, completion: (()->Void)?) {

        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        // Table View update on row / section
        beginUpdates()
        update()
        endUpdates()

        CATransaction.commit()
    }

}

Ceci est utilisé comme ceci:

// Insert in the tableView the section we just added in sections
self.tableView.performUpdate({
            self.tableView.insertSections([newSectionIndex], with: UITableViewRowAnimation.top)

        }, completion: {
            // Scroll to next section
            let nextSectionIndexPath = IndexPath(row: 0, section: newSectionIndex)
            self.tableView.scrollToRow(at: nextSectionIndexPath, at: .top, animated: true)
        })
Frédéric Adda
la source
Réponse géniale! c'est l'une des raisons pour lesquelles j'aime Swift
Gianni Carlo
@GianniCarlo, vous pouvez également le faire dans ObjC
CyberMew
@CyberMew oui, mais créer une catégorie a toujours été une telle douleur, spécialement en raison des longs noms des fichiers supplémentaires
Gianni Carlo
il n'est disponible que dans iOS 11, comment l'utiliser dans iOS 10?
kemdo
@kemdo Pourquoi dites-vous qu'il n'est disponible que dans iOS 11? Tout ici est iOS 2+ sauf setCompletionBlockqui est iOS 4+
Frédéric Adda
25

Raccourcir la bonne réponse de Brent , pour au moins iOS 7, vous pouvez envelopper tout cela de manière laconique dans un appel [UIView animateWithDuration: delay: options: animations: completion:]:

[UIView animateWithDuration:10 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
  [self.tableView beginUpdates];
  [self.tableView endUpdates];
} completion:^(BOOL finished) {
  // completion code
}];

cependant, je n'arrive pas à remplacer la courbe d'animation par défaut d'autre chose que EaseInOut.

visnu
la source
2
Lorsque vous effectuez une insertion de ligne de cette façon, ou à la manière de @ Brent, bien que la durée soit respectée, l'UITableViewRowAnimation ne semble pas être respectée et semble toujours s'animer de haut en bas, même lorsque je spécifie, par exemple UITableViewRowAnimationLeft. Test sur iOS 8.4 - quelqu'un a-t-il une solution?
Danny
23

Voici une version Swift de la réponse de Karwag

    CATransaction.begin()
    tableView.beginUpdates()
    CATransaction.setCompletionBlock { () -> Void in
        // your code here
    }
    tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
    tableView.endUpdates()
    CATransaction.commit()
primulaveris
la source
6

Pour moi, j'en avais besoin pour une collectionView. J'ai fait une simple extension pour résoudre ce problème:

extension UICollectionView {

    func reloadSections(sections: NSIndexSet, completion: () -> Void){
        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        self.reloadSections(sections)

        CATransaction.commit()
    }

}
Antoine
la source
1

Comme la performBatchméthode de tableView est disponible à partir d' iOS 11 uniquement, vous pouvez utiliser l'extension suivante:

extension UITableView {
func performUpdates(_ updates: @escaping () -> Void, completion: @escaping (Bool) -> Void) {
        if #available(iOS 11.0, *) {
            self.performBatchUpdates({
                updates()
            }, completion: completion)
        } else {
            CATransaction.begin()
            beginUpdates()
            CATransaction.setCompletionBlock {
                completion(true)
            }
            updates()
            endUpdates()
            CATransaction.commit()
        }
    }
}
Stanislau Baranouski
la source
-8

Vous pouvez essayer d'encapsuler le insertRowsAtIndexPath dans un

- (void)beginUpdates
- (void)endUpdates

transaction, puis faites le flash par la suite.

Jordan
la source
Voir la réponse de Karwag ci-dessus. Vous devez résoudre le problème de ce qui compte comme «après».
JLundell