Duração da animação de linha UITableView e retorno de chamada de conclusão


98

Existe uma maneira de especificar a duração das animações de linha UITableView ou de obter um retorno de chamada quando a animação for concluída?

O que eu gostaria de fazer é piscar os indicadores de rolagem após a conclusão da animação. Fazer o flash antes disso não faz nada. Até agora, a solução que tenho é atrasar meio segundo (que parece ser a duração padrão da animação), ou seja:

[self.tableView insertRowsAtIndexPaths:newRows
                      withRowAnimation:UITableViewRowAnimationFade];
[self.tableView performSelector:@selector(flashScrollIndicators)
                     withObject:nil
                     afterDelay:0.5];

Eu não tentei sozinho, mas talvez isso pudesse resolver, com algum tratamento de caminho de índice:- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
Kalle,

Respostas:


3

Hoje em dia, se você quiser fazer isso, há uma nova função a partir do iOS 11 :

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

Em fechamentos de atualizações, você coloca o mesmo código que na seção beginUpdates () / endUpdates. E a conclusão é executada após todas as animações.


Isso é ótimo. Eu não tinha notado essa adição.
Daniel Dickison

207

Acabei de encontrar isso. Veja como fazer:

Objective-C

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

2
Novamente, funciona perfeitamente aqui. iOS6 e tudo. Este é um mecanismo compatível com SDK adequado para substituir propriedades em animações padrão. Talvez você tenha animações adicionais de longa duração dentro de sua CATransaction? Eles ficam aninhados, você sabe.
karwag

1
Funciona muito bem para mim no iOS6. Obrigado por isso!
Aron

5
setAnimationDurationnão parece afetar a duração da inserção / exclusão. iOS 6
Tom Redman

2
alguma sugestão de como alterar a duração? CATransaction setAnimationDuration: não parece fazer diferença.
Jeff Grimes

5
Funciona bem para mim também no iOS 5.1.1, 6.1, 7.0; Mas, se você precisar obter um novo tableView.contentSize após a animação (como foi no meu caso), você deve usar [self performSelectorOnMainThread: withObject: waitUntilDone:]; em setCompletionBlock para chamar seu delegado no próximo runloop. se você chamar seu delegado diretamente, sem performSelectorOnMainThread, obterá o valor antigo para tableView.contentSize.
slamor de

38

Expandindo a boa resposta de karwag , observe que no iOS 7, cercar a CATransaction com uma animação UIView oferece controle da duração da animação da mesa.

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

A duração da animação UIView não tem efeito no iOS 6. Talvez as animações de mesa do iOS 7 sejam implementadas de forma diferente, no nível UIView.


A duração da animação parece ser ignorada.
Dustin

26

Esse é um truque muito útil! Eu escrevi uma extensão UITableView para evitar escrever coisas CATransaction o tempo todo.

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

}

Isso é usado assim:

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

Resposta incrível! esta é uma das razões pelas quais eu amo Swift
Gianni Carlo

@GianniCarlo você também pode fazer isso no ObjC
CyberMew

@CyberMew sim, mas criar uma categoria sempre foi uma dor, especialmente devido aos nomes longos dos arquivos extras
Gianni Carlo

só está disponível no ios 11, como usá-lo no ios 10?
kemdo

@kemdo Por que você diz que só está disponível no iOS 11? Tudo aqui é iOS 2+ exceto o setCompletionBlockiOS 4+
Frédéric Adda

25

Encurtando a boa resposta de Brent , pelo menos para iOS 7 você pode envolver tudo isso concisamente em uma chamada [UIView animateWithDuration: delay: options: animations: Complementation:]:

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

no entanto, não consigo substituir a curva de animação padrão de qualquer outra coisa que não seja EaseInOut.


2
Ao fazer uma inserção de linha desta forma, ou da maneira de @Brent, embora a duração seja respeitada, o UITableViewRowAnimation não parece ser respeitado e sempre parece animar de cima para baixo, mesmo quando eu especifico, por exemplo UITableViewRowAnimationLeft. Testando no iOS 8.4 - alguém tem uma solução?
Danny

23

Aqui está uma versão rápida da resposta de Karwag

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

6

Para mim, eu precisava disso para um collectionView. Fiz uma extensão simples para resolver isso:

extension UICollectionView {

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

        self.reloadSections(sections)

        CATransaction.commit()
    }

}

1

Como o performBatchmétodo de tableView está disponível a partir do iOS 11 apenas, você pode usar a seguinte extensão:

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

-8

Você pode tentar envolver o insertRowsAtIndexPath em um

- (void)beginUpdates
- (void)endUpdates

transação, em seguida, faça o flash depois.


Veja a resposta de karwag acima. Você precisa resolver o problema do que conta como "depois".
JLundell
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.