Wie erkenne ich, dass die Animation bei UITableView beginUpdates / endUpdates beendet wurde?

116

Ich füge eine Tabellenzelle mit insertRowsAtIndexPaths/deleteRowsAtIndexPathsWrap-In ein / lösche sie beginUpdates/endUpdates. Ich benutze auch beginUpdates/endUpdatesbeim Anpassen von rowHeight. Alle diese Vorgänge sind standardmäßig animiert.

Wie kann ich feststellen, dass die Animation bei Verwendung beendet wurde beginUpdates/endUpdates?

Pixelfreak
quelle
1
Zu Ihrer Information: Dies gilt -scrollToRowAtIndexPath:atScrollPosition:animated:auch für.
Ben Lachman

Antworten:

292

Was ist damit?

[CATransaction begin];

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

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

[CATransaction commit];

Dies funktioniert, weil die tableView- CALayerAnimationen intern Animationen verwenden . Das heißt, sie fügen die Animationen zu jedem offenen hinzu CATransaction. Wenn kein Open CATransactionvorhanden ist (der Normalfall), wird implizit eines gestartet, das am Ende des aktuellen Runloops beendet wird. Aber wenn Sie selbst einen beginnen, wie es hier gemacht wird, dann wird es diesen verwenden.

Rudolf Adamkovič
quelle
7
Hat bei mir nicht funktioniert. Der Abschlussblock wird aufgerufen, während die Animation noch ausgeführt wird.
Mrvincenzo
2
@ MrVincenzo Hast du das completionBlockvorher eingestellt beginUpdatesund magst du endUpdatesim Snippet?
Rudolf Adamkovič
2
[CATransaction commit]sollte nach nicht vorher aufgerufen werden [tableView endUpdates].
Rudolf Adamkovič
6
Der Abschlussblock wird niemals aufgerufen, wenn eine Zelle eine Ansicht mit Animationen enthält, dh UIActivityIndicatorView.
Marktspitze
3
Nach all dieser Zeit wusste ich das nie. Reines Gold !! Nichts schlimmeres als zu sehen, wie eine nette Animation mit dem notwendigen Übel von tableView.reloadData () getötet wird.
DogCoffee
31

Schnelle Version


CATransaction.begin()

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

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
Michael
quelle
6

Wenn Sie auf iOS 11 und höher abzielen, sollten Sie UITableView.performBatchUpdates(_:completion:)stattdessen Folgendes verwenden :

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

Eine mögliche Lösung könnte darin bestehen, von der UITableView zu erben, die Sie aufrufen, endUpdatesund diese zu überschreiben setContentSizeMethod, da UITableView die Inhaltsgröße an die hinzugefügten oder entfernten Zeilen anpasst. Dieser Ansatz sollte auch funktionieren reloadData.

Um sicherzustellen, dass eine Benachrichtigung erst nach dem endUpdatesAufruf gesendet wird, kann man dort auch endUpdatesein Flag überschreiben und setzen.

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

Sie können Ihre Operation (en) wie folgt in den UIView-Animationsblock einschließen:

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

Credits an https://stackoverflow.com/a/12905114/634940 .

Zdenek
quelle
4
Dieser Code hat bei mir nicht funktioniert. Der Abschlussblock wurde nach endUpdates, aber bevor die Animationen abgeschlossen waren, aufgerufen .
Tim Camber
1
Das funktioniert nicht. Die Tabellenansicht-Animation funktionierte beim Anwenden dieses Beispiels nicht mehr.
Primoz990
Versuchen Sie, die Dauer zu verlängern (wie 0,3 Sekunden) - es sollte funktionieren (es hat bei mir funktioniert)
emem
1

Ich habe noch keine gute Lösung gefunden (ohne UITableView). Ich habe mich performSelector:withObject:afterDelay:für jetzt entschieden. Nicht ideal, aber erledigt den Job.

UPDATE : Es sieht so aus, als könnte ich es scrollViewDidEndScrollingAnimation:für diesen Zweck verwenden (dies ist spezifisch für meine Implementierung, siehe Kommentar).

Pixelfreak
quelle
1
scrollViewDidEndScrollingAnimationwird nur als Antwort auf setContentOffsetundscrollRectToVisible
Samvermette
@samvermette Nun, in meinem Fall, wenn beginUpdates / endUpdates aufgerufen werden, animiert die UITableView immer den Bildlauf. Dies ist jedoch spezifisch für meine Implementierung, daher stimme ich zu, dass dies nicht die beste Antwort ist, aber für mich funktioniert.
Pixelfreak
Es scheint möglich zu sein, Aktualisierungen in einen UIView-Animationsblock einzuschließen. Siehe meine Antwort unten : stackoverflow.com/a/14305039/634940 .
Zdenek
0

Sie können tableView:willDisplayCell:forRowAtIndexPath:wie folgt verwenden :

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

Dies wird jedoch auch aufgerufen, wenn eine Zelle, die sich bereits in der Tabelle befindet, vom Bildschirm auf den Bildschirm verschoben wird, sodass sie möglicherweise nicht genau das ist, wonach Sie suchen. Ich habe gerade alle Methoden UITableViewund UIScrollViewdelegate-Methoden durchgesehen und es scheint nichts zu geben, was zu handhaben ist, nachdem eine Zelle eingefügt wurde.


Rufen Sie einfach die Methode auf, die aufgerufen werden soll, wenn die Animation nach dem endet 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];
    }
}
chown
quelle
Danke, Chown. Ich glaube nicht, dass willDisplayCelles funktionieren wird. Ich brauche es nach der Animation, weil ich erst ein visuelles Update machen muss, nachdem sich alles beruhigt hat.
Pixelfreak
np @pixelfreak, wenn ich mir einen anderen Weg überlege, werde ich es dich wissen lassen.
Chown