Czas trwania animacji wiersza UITableView i wywołanie zwrotne zakończenia

98

Czy istnieje sposób określenia czasu trwania animacji wiersza UITableView lub uzyskania wywołania zwrotnego po zakończeniu animacji?

To, co chciałbym zrobić, to migać wskaźnikami przewijania po zakończeniu animacji. Wykonanie flashowania przedtem nic nie robi. Jak dotąd obejściem, które mam, jest opóźnienie o pół sekundy (wydaje się, że jest to domyślny czas trwania animacji), tj .:

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];
Daniel Dickison
źródło
Sam nie próbowałem, ale może to się uda, z pewną obsługą ścieżki indeksu:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle

Odpowiedzi:

3

Obecnie, jeśli chcesz to zrobić, pojawiła się nowa funkcja począwszy od iOS 11 :

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

W zamknięciach aktualizacji umieszczasz ten sam kod, co w sekcji beginUpdates () / endUpdates. Zakończenie jest wykonywane po wszystkich animacjach.

Michał Ziobro
źródło
To jest świetne. Nie zauważyłem tego dodatku.
Daniel Dickison,
207

Właśnie to spotkałem. Oto jak to zrobić:

Cel C

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

Szybki

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock {
    // Code to be executed upon completion
}
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()
karwag
źródło
2
Ponownie działa tutaj bez zarzutu. iOS6 i wszystkie. Jest to poprawny mechanizm obsługiwany przez SDK do zastępowania właściwości w domyślnych animacjach. Być może masz dodatkowe, dłużej działające animacje wewnątrz swojej transakcji CAT? Wiesz, że się zagnieżdżają.
karwag
1
U mnie działa świetnie w iOS6. Dziękuję za to!
Aron,
5
setAnimationDurationnie wydaje się mieć wpływu na czas trwania wstawiania / usuwania. iOS 6
Tom Redman,
2
jakieś sugestie, jak zmienić czas trwania? CATransaction setAnimationDuration: wydaje się, że nie robi różnicy.
Jeff Grimes,
5
U mnie też działa dobrze w iOS 5.1.1, 6.1, 7.0; Ale jeśli chcesz uzyskać nowy tableView.contentSize po animacji (tak jak to było w moim przypadku), musisz użyć [self performSelectorOnMainThread: withObject: waitUntilDone:]; w setCompletionBlock w celu wywołania delegata w następnym runloop. jeśli wywołasz delegata bezpośrednio, bez performSelectorOnMainThread, otrzymasz starą wartość dla tableView.contentSize.
slamour
38

Rozwijając dobrą odpowiedź Karwaga , zauważ, że na iOS 7 otoczenie transakcji CAT animacją UIView zapewnia kontrolę nad czasem trwania animacji tabeli.

[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];

Czas trwania animacji UIView nie ma wpływu na iOS 6. Być może animacje tabel w iOS 7 są implementowane inaczej, na poziomie UIView.

Brent
źródło
Wydaje się, że czas trwania animacji jest ignorowany.
Dustin
26

To cholernie przydatna sztuczka! Napisałem rozszerzenie UITableView, aby uniknąć ciągłego pisania elementów CATransaction.

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

}

Jest to używane w następujący sposób:

// 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
źródło
Świetna odpowiedź! to jeden z powodów, dla których uwielbiam Swift
Gianni Carlo,
@GianniCarlo możesz to zrobić również w ObjC
CyberMew
@CyberMew tak, ale tworzenie kategorii zawsze było uciążliwe, szczególnie ze względu na długie nazwy dodatkowych plików
Gianni Carlo
jest dostępny tylko w ios 11, jak go używać w ios 10?
kemdo
@kemdo Dlaczego mówisz, że jest dostępny tylko w iOS 11? Wszystko tutaj to iOS 2+, z wyjątkiem setCompletionBlockiOS 4+
Frédéric Adda
25

Skracając dobrą odpowiedź Brenta , przynajmniej dla iOS 7 możesz zawinąć to wszystko zwięźle w [UIView animateWithDuration: delay: options: animations: complete:] call:

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

jednak wydaje mi się, że nie mogę zastąpić domyślnej krzywej animacji z niczego innego niż EaseInOut.

visnu
źródło
2
Podczas wstawiania wiersza w ten sposób lub w sposób @ Brent, chociaż czas trwania jest przestrzegany, UITableViewRowAnimation nie wydaje się być przestrzegany i zawsze wydaje się być animowany z góry na dół, nawet jeśli określę, na przykład UITableViewRowAnimationLeft. Testowanie na iOS 8.4 - ktoś ma rozwiązanie?
Danny
23

Oto szybka wersja odpowiedzi karwaga

    CATransaction.begin()
    tableView.beginUpdates()
    CATransaction.setCompletionBlock { () -> Void in
        // your code here
    }
    tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
    tableView.endUpdates()
    CATransaction.commit()
primulaveris
źródło
6

Dla mnie potrzebowałem tego dla collectionView. Zrobiłem proste rozszerzenie, aby rozwiązać ten problem:

extension UICollectionView {

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

        self.reloadSections(sections)

        CATransaction.commit()
    }

}
Antoine
źródło
1

Ponieważ performBatchmetoda tableView jest dostępna tylko od iOS 11 , możesz użyć następującego rozszerzenia:

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

Możesz spróbować opakować insertRowsAtIndexPath w plik

- (void)beginUpdates
- (void)endUpdates

transakcja, a następnie wykonaj flashowanie.

Jordania
źródło
Zobacz odpowiedź Karwaga powyżej. Musisz rozwiązać problem tego, co liczy się jako „później”.
JLundell