Duración de la animación de fila de UITableView y devolución de llamada de finalización

98

¿Hay alguna manera de especificar la duración de las animaciones de fila de UITableView o de obtener una devolución de llamada cuando se completa la animación?

Lo que me gustaría hacer es hacer parpadear los indicadores de desplazamiento después de que se complete la animación. Hacer el flash antes de eso no hace nada. Hasta ahora, la solución que tengo es retrasar medio segundo (que parece ser la duración predeterminada de la animación), es decir:

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];
Daniel Dickison
fuente
No lo he probado yo mismo, pero tal vez esto podría hacerlo, con un poco de manejo de ruta de índice:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle

Respuestas:

3

Hoy en día, si desea hacer esto, hay una nueva función a partir de iOS 11 :

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

En los cierres de actualizaciones, coloca el mismo código que en la sección beginUpdates () / endUpdates. Y la finalización se ejecuta después de todas las animaciones.

Michał Ziobro
fuente
Esto es genial. No había notado esta adición.
Daniel Dickison
207

Acabo de encontrarme con esto. He aquí cómo hacerlo:

C objetivo

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

Rápido

CATransaction.begin()
tableView.beginUpdates()
CATransaction.setCompletionBlock {
    // Code to be executed upon completion
}
tableView.insertRowsAtIndexPaths(indexArray, withRowAnimation: .Top)
tableView.endUpdates()
CATransaction.commit()
karwag
fuente
2
Nuevamente, funciona perfectamente aquí. iOS6 y todo. Este es un mecanismo compatible con SDK adecuado para invalidar las propiedades en las animaciones predeterminadas. ¿Quizás tiene animaciones adicionales de mayor duración dentro de su CATransaction? Se anidan, ya sabes.
karwag
1
Funciona muy bien para mí en iOS6. ¡Gracias por eso!
Aron
5
setAnimationDurationno parece afectar la duración de inserción / eliminación. iOS 6
Tom Redman
2
¿Alguna sugerencia sobre cómo cambiar la duración? CATransaction setAnimationDuration: no parece hacer una diferencia.
Jeff Grimes
5
También me funciona bien en iOS 5.1.1, 6.1, 7.0; Pero, si necesita obtener un nuevo tableView.contentSize después de la animación (como fue en mi caso), debe usar [self performSelectorOnMainThread: withObject: waitUntilDone:]; en setCompletionBlock para llamar a su delegado en el próximo runloop. si llama a su delegado directamente, sin performSelectorOnMainThread, obtiene el valor anterior para tableView.contentSize.
slamor
38

Ampliando la excelente respuesta de karwag , tenga en cuenta que en iOS 7, rodear la transacción CAT con una animación UIView ofrece control de la duración de la animación de la tabla.

[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 duración de la animación de UIView no tiene efecto en iOS 6. Quizás las animaciones de tabla de iOS 7 se implementan de manera diferente, en el nivel de UIView.

Brent
fuente
La duración de la animación parece ignorarse.
Dustin
26

¡Ese es un truco increíblemente útil! Escribí una extensión UITableView para evitar escribir cosas CATransaction todo el tiempo.

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

}

Esto se usa así:

// 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
fuente
¡Respuesta impresionante! esta es una de las razones por las que amo a Swift
Gianni Carlo
@GianniCarlo también puedes hacer esto en ObjC
CyberMew
@CyberMew sí, pero crear una categoría siempre ha sido un dolor, especialmente debido a los nombres largos de los archivos adicionales
Gianni Carlo
solo está disponible en ios 11, ¿cómo usarlo en ios 10?
kemdo
@kemdo ¿Por qué dices que solo está disponible en iOS 11? Todo aquí es iOS 2+ excepto setCompletionBlockque es iOS 4+
Frédéric Adda
25

Acortando la buena respuesta de Brent , para al menos iOS 7 puede envolver todo esto concisamente en una llamada [UIView animateWithDuration: delay: options: animations: complete:]:

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

sin embargo, parece que no puedo anular la curva de animación predeterminada de otra cosa que no sea EaseInOut.

visnu
fuente
2
Al hacer una inserción de fila de esta manera, o la manera de @ Brent, aunque se respeta la duración, la UITableViewRowAnimation no parece respetarse y siempre parece animarse de arriba hacia abajo, incluso cuando yo especifique, por ejemplo UITableViewRowAnimationLeft. Pruebas en iOS 8.4: ¿alguien tiene una solución?
Danny
23

Aquí hay una versión rápida de la respuesta de karwag.

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

Para mí, necesitaba esto para un collectionView. Hice una extensión simple para resolver esto:

extension UICollectionView {

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

        self.reloadSections(sections)

        CATransaction.commit()
    }

}
Antoine
fuente
1

Como el performBatchmétodo de tableView está disponible solo a partir de iOS 11 , puede usar la siguiente extensión:

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
fuente
-8

Podría intentar envolver el insertRowsAtIndexPath en un

- (void)beginUpdates
- (void)endUpdates

transacción, luego haga el flash después.

Jordán
fuente
Vea la respuesta de karwag arriba. Necesita resolver el problema de lo que cuenta como "después".
JLundell