Come rilevare che l'animazione è terminata su UITableView beginUpdates / endUpdates?


116

Sto inserendo / cancellando la cella della tabella usando insertRowsAtIndexPaths/deleteRowsAtIndexPathsavvolto in beginUpdates/endUpdates. Sto anche usando beginUpdates/endUpdatesquando aggiusto rowHeight. Tutte queste operazioni sono animate per impostazione predefinita.

Come posso rilevare che l'animazione è terminata durante l'utilizzo beginUpdates/endUpdates?


1
FYI: Questo vale anche per -scrollToRowAtIndexPath:atScrollPosition:animated:.
Ben Lachman

Risposte:


292

Che dire di questo?

[CATransaction begin];

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

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

[CATransaction commit];

Questo funziona perché le animazioni tableView utilizzano CALayerinternamente le animazioni. Cioè, aggiungono le animazioni a qualsiasi apertura CATransaction. Se non CATransactionesiste alcun open (il caso normale), ne viene avviato uno implicitamente, che termina alla fine del ciclo di esecuzione corrente. Ma se ne inizi uno tu stesso, come si fa qui, allora userà quello.


7
Non ha funzionato per me. Il blocco di completamento viene chiamato mentre l'animazione è ancora in esecuzione.
mrvincenzo

2
@MrVincenzo Hai impostato completionBlockprima beginUpdatese endUpdatescome nello snippet?
Rudolf Adamkovič

2
[CATransaction commit]dovrebbe essere chiamato dopo non prima [tableView endUpdates].
Rudolf Adamkovič

6
Il blocco di completamento non viene mai chiamato se una cella contiene una vista con animazioni, ad esempio UIActivityIndicatorView.
markturnip

3
Dopo tutto questo tempo non l'ho mai saputo. Oro zecchino !! Niente di peggio che vedere una bella animazione che viene uccisa con il male necessario di tableView.reloadData ().
DogCoffee

31

Versione rapida


CATransaction.begin()

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

tableView.beginUpdates()
tableView.endUpdates()

CATransaction.commit()

6

Se scegli come target iOS 11 e versioni successive, dovresti utilizzare UITableView.performBatchUpdates(_:completion:)invece:

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

5

Una possibile soluzione potrebbe essere quella di ereditare da UITableView su cui chiami endUpdatese sovrascriverlo setContentSizeMethod, poiché UITableView regola la dimensione del contenuto in modo che corrisponda alle righe aggiunte o rimosse. Questo approccio dovrebbe funzionare anche per reloadData.

Per garantire che una notifica venga inviata solo dopo che endUpdatesè stata chiamata, si potrebbe anche sovrascrivere endUpdatese impostare un flag lì.

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

5

Puoi racchiudere le tue operazioni nel blocco di animazione UIView in questo modo:

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

Crediti a https://stackoverflow.com/a/12905114/634940 .


4
Questo codice non ha funzionato per me. Il blocco di completamento è stato chiamato dopo endUpdates, ma prima che le animazioni fossero state completate.
Tim Camber

1
Questo non funziona. L'animazione tableview ha smesso di funzionare durante l'applicazione di questo esempio.
Primoz990

Prova a prolungare la durata (come 0,3 sec) - dovrebbe funzionare (ha funzionato per me)
emem

1

Non ho ancora trovato una buona soluzione (a parte la sottoclasse UITableView). Ho deciso di usare performSelector:withObject:afterDelay:per ora. Non è l'ideale, ma porta a termine il lavoro.

AGGIORNAMENTO : Sembra che posso usare scrollViewDidEndScrollingAnimation:per questo scopo (questo è specifico per la mia implementazione, vedi commento).


1
scrollViewDidEndScrollingAnimationviene chiamato solo in risposta a setContentOffsetescrollRectToVisible
samvermette

@samvermette Ebbene, nel mio caso, quando vengono chiamati beginUpdates / endUpdates, UITableView si anima sempre con scorrimento. Ma questo è specifico per la mia implementazione, quindi sono d'accordo che non è la risposta migliore ma funziona per me.
pixelfreak

Sembra che sia possibile racchiudere gli aggiornamenti in un blocco di animazione UIView. Vedi la mia risposta qui sotto: stackoverflow.com/a/14305039/634940 .
Zdenek

0

Puoi usare tableView:willDisplayCell:forRowAtIndexPath:come:

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

Ma questo verrà chiamato anche quando una cella che è già nella tabella si sposta da fuori dallo schermo a sullo schermo, quindi potrebbe non essere esattamente quello che stai cercando. Ho appena esaminato tutti i metodi UITableViewe UIScrollViewdelegate e non sembra esserci nulla da gestire subito dopo l'inserimento dell'animazione di una cella.


Perché non chiamare semplicemente il metodo che desideri venga chiamato quando l'animazione termina dopo il 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];
    }
}

Grazie, chown. Non credo willDisplayCellche funzionerà. Ho bisogno che accada dopo l'animazione perché devo fare un aggiornamento visivo solo dopo che tutto si è sistemato.
pixelfreak

np @pixelfreak, se penso a un altro modo per farlo te lo farò sapere.
chown
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.