Jak wykryć, że animacja zakończyła się w UITableView beginUpdates / endUpdates?

116

Wstawiam / usuwam komórkę tabeli za pomocą insertRowsAtIndexPaths/deleteRowsAtIndexPathszawiniętej beginUpdates/endUpdates. Używam również beginUpdates/endUpdatespodczas dostosowywania rowHeight. Wszystkie te operacje są domyślnie animowane.

Jak mogę wykryć, że animacja zakończyła się podczas używania beginUpdates/endUpdates?

pixelfreak
źródło
1
FYI: Dotyczy to -scrollToRowAtIndexPath:atScrollPosition:animated:również.
Ben Lachman,

Odpowiedzi:

292

A co z tym?

[CATransaction begin];

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

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

[CATransaction commit];

Działa to, ponieważ animacje tableView używają CALayeranimacji wewnętrznie. Oznacza to, że dodają animacje do każdego otwartego CATransaction. Jeśli nie CATransactionistnieje żaden otwarty (normalny przypadek), to jest niejawnie rozpoczynany, który kończy się na końcu bieżącego pętli. Ale jeśli sam zaczniesz, tak jak tutaj, to użyje tego.

Rudolf Adamkovič
źródło
7
Nie działa dla mnie. Blok ukończenia jest wywoływany, gdy animacja jest nadal uruchomiona.
mrvincenzo
2
@MrVincenzo Czy ustawiłeś completionBlockprzed beginUpdatesi endUpdatesjak we fragmencie?
Rudolf Adamkovič
2
[CATransaction commit]należy wywołać po nie wcześniej [tableView endUpdates].
Rudolf Adamkovič
6
Blok uzupełniania nigdy nie jest wywoływany, jeśli komórka zawiera widok z animacjami, tj. UIActivityIndicatorView.
markturnip
3
Po całym tym czasie nigdy tego nie wiedziałem. Czyste złoto !! Nie ma nic gorszego niż oglądanie ładnej animacji zabijanej złem koniecznym tableView.reloadData ().
DogCoffee
31

Szybka wersja


CATransaction.begin()

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

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()
Michael
źródło
6

Jeśli celujesz w system iOS 11 lub nowszy, UITableView.performBatchUpdates(_:completion:)zamiast tego użyj :

tableView.performBatchUpdates({
    // delete some cells
    // insert some cells
}, completion: { finished in
    // animation complete
})
Robert
źródło
5

Możliwym rozwiązaniem może być dziedziczenie z UITableView, na którym wywołujesz endUpdatesi zastępujesz go setContentSizeMethod, ponieważ UITableView dostosowuje rozmiar zawartości, aby pasował do dodanych lub usuniętych wierszy. To podejście powinno również działać w przypadku reloadData.

Aby mieć pewność, że powiadomienie zostanie wysłane dopiero po endUpdateswywołaniu, można również nadpisać endUpdatesi ustawić tam flagę.

// 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
źródło
5

Możesz ująć swoje operacje w bloku animacji UIView w następujący sposób:

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

Kredyty na https://stackoverflow.com/a/12905114/634940 .

Zdenek
źródło
4
Ten kod nie działa dla mnie. Blok ukończenia został wywołany później endUpdates, ale przed zakończeniem animacji.
Tim Camber
1
To nie działa. Animacja widoku tabeli przestała działać podczas stosowania tego przykładu.
Primoz990
Spróbuj wydłużyć czas trwania (np. 0,3 sekundy) - powinien działać (dla mnie zadziałał)
emem
1

Nie znalazłem jeszcze dobrego rozwiązania (bez podklasy UITableView). Postanowiłem na razie użyć performSelector:withObject:afterDelay:. Nie jest idealny, ale wykonuje swoją pracę.

AKTUALIZACJA : Wygląda na to, że mogę użyć scrollViewDidEndScrollingAnimation:do tego celu (jest to specyficzne dla mojej implementacji, patrz komentarz).

pixelfreak
źródło
1
scrollViewDidEndScrollingAnimationjest wywoływany tylko w odpowiedzi na setContentOffsetiscrollRectToVisible
samvermette
@samvermette Cóż, w moim przypadku, gdy wywoływane są beginUpdates / endUpdates, UITableView zawsze animate-scroll. Ale to jest specyficzne dla mojej implementacji, więc zgadzam się, że to nie jest najlepsza odpowiedź, ale działa dla mnie.
pixelfreak
Wydaje się, że jest możliwe dołączenie aktualizacji do bloku animacji UIView. Zobacz moją odpowiedź poniżej: stackoverflow.com/a/14305039/634940 .
Zdenek
0

Możesz użyć tableView:willDisplayCell:forRowAtIndexPath:takich jak:

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

Ale zostanie to również wywołane, gdy komórka, która już znajduje się w tabeli, przesunie się z ekranu poza ekran do ekranu, więc może nie być dokładnie tym, czego szukasz. Właśnie przejrzałem wszystkie UITableViewiUIScrollView delegate i wydaje się, że nie ma nic do obsłużenia zaraz po wstawieniu komórki do animacji.


Dlaczego po prostu nie wywołać metody, którą chcesz wywołać, gdy animacja zakończy się po 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
źródło
Dzięki, chown. Nie myśl, willDisplayCellże zadziała. Potrzebuję, aby stało się to po animacji, ponieważ muszę dokonać aktualizacji wizualnej tylko wtedy, gdy wszystko się ułoży.
pixelfreak
np @pixelfreak, jeśli wymyślę inny sposób na zrobienie tego, dam ci znać.
chown