Tenha um reloadData para um UITableView animado ao alterar

183

Eu tenho um UITableView que possui dois modos. Quando alternamos entre os modos, tenho um número diferente de seções e células por seção. Idealmente, ele faria alguma animação interessante quando a mesa cresce ou diminui.

Aqui está o código que eu tentei, mas ele não faz nada:

CGContextRef context = UIGraphicsGetCurrentContext(); 
[UIView beginAnimations:nil context:context]; 
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 
[UIView setAnimationDuration:0.5]; 

[self.tableView reloadData];
[UIView commitAnimations];

Alguma idéia de como eu poderia fazer isso?

Vertexwahn
fonte

Respostas:

400

Na verdade, é muito simples:

[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];

A partir da documentação :

A chamada desse método faz com que a exibição da tabela solicite à sua fonte de dados novas células para as seções especificadas. A exibição de tabela anima a inserção de novas células, assim como anima as células antigas.

dmarnel
fonte
13
E simplesmente a melhor resposta nesta página!
Matthias D
41
Isso não está totalmente correto, porque apenas recarregará a primeira seção. Se a sua tabela tiver várias seções, isso não funcionará
Nosrettap:
4
É possível que você receba uma exceção se nenhum dado foi alterado. Veja minha resposta
Matej
8
@Nosrettap: se você quiser ter mais ou todas as seções do seu recarregamento tableView, tudo que você tem a fazer é estender sua NSIndexSet com todos os índices de seção, que precisa ser atualizado, bem
JonEasy
Versão Swift: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)Notavelmente, porém, ele atualiza apenas a Seção 0, que é a primeira seção padrão.
Kbpontius 3/03
264

Você pode querer usar:

Objetivo-C

[UIView transitionWithView: self.tableView
                  duration: 0.35f
                   options: UIViewAnimationOptionTransitionCrossDissolve
                animations: ^(void)
 {
      [self.tableView reloadData];
 }
                completion: nil];

Rápido

UIView.transitionWithView(tableView,
                          duration: 0.35,
                          options: .TransitionCrossDissolve,
                          animations:
{ () -> Void in
    self.tableView.reloadData()
},
                          completion: nil);

Swift 3, 4 e 5

UIView.transition(with: tableView,
                  duration: 0.35,
                  options: .transitionCrossDissolve,
                  animations: { self.tableView.reloadData() }) // left out the unnecessary syntax in the completion block and the optional completion parameter

Sem aborrecimentos. : D

Você também pode usar qualquer um dos UIViewAnimationOptionTransitionsefeitos desejados:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom
Kenn Cal
fonte
2
Isso é útil se o estado inicial / final da visualização da tabela for muito diferente (e seria complexo calcular as seções e linhas para adicionar / remover), mas você terá algo menos dissonante que o não animado.
Ben Packard
1
Essa animação não é tão boa quanto recarregar a visualização de tabela usando seus métodos internos, mas, diferentemente do método de classificação mais alta mencionado aqui, este funciona quando você tem várias seções.
Mark Bridges
1
Uau, depois de tentar todo o resto, isso é desafiadoramente perfeito para mim #
Lucas Goossen
1
@MarkBridges a resposta mais alta nominal funciona com várias seções :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
Lobianco
2
@ Anthony Não funciona se o estado final tiver mais / menos seções que o estado inicial. Então você teria que rastrear manualmente quais seções foram adicionadas / excluídas, o que é um grande aborrecimento.
Nikolovski
78

Tenha mais liberdade usando CATransitionclasse.

Não se limita ao desbotamento, mas também pode fazer movimentos.


Por exemplo:

(não esqueça de importar QuartzCore)

CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transition.fillMode = kCAFillModeForwards;
transition.duration = 0.5;
transition.subtype = kCATransitionFromBottom;

[[self.tableView layer] addAnimation:transition forKey:@"UITableViewReloadDataAnimationKey"];

Altere typepara corresponder às suas necessidades, como kCATransitionFadeetc.

Implementação no Swift:

let transition = CATransition()
transition.type = kCATransitionPush
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.fillMode = kCAFillModeForwards
transition.duration = 0.5
transition.subtype = kCATransitionFromTop
self.tableView.layer.addAnimation(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()

Referência para CATransition

Swift 5:

let transition = CATransition()
transition.type = CATransitionType.push
transition.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
transition.fillMode = CAMediaTimingFillMode.forwards
transition.duration = 0.5
transition.subtype = CATransitionSubtype.fromTop
self.tableView.layer.add(transition, forKey: "UITableViewReloadDataAnimationKey")
// Update your data source here
self.tableView.reloadData()
Matej
fonte
isso anima a tabela como um todo, em vez de linhas / seções únicas.
Agos
@ Agos Ainda responde à pergunta. Uma pergunta "Como recarregar apenas uma linha" tem uma resposta bastante diferente, como aplicar a animação à layerpropriedade de a UITableViewCell.
21313 Matej
@matejkramny Eu esperava que a tabela animasse apenas as diferentes linhas (conforme mencionado na pergunta), mas esse método empurra toda a tabela de uma só vez. Talvez esteja faltando alguma coisa?
Agos
@ Agos hmm não, é suposto fazer isso. Ele não pode fazer apenas metade da tabela, pois o QuartzCore modifica a exibição diretamente. Você pode tentar obter as células da exibição de tabela, e em seguida, aplicar esta animação a cada embora (mas não tenho certeza que ele iria trabalhar)
Matej
2
funcionou como um encanto com o kCATransitionFade Obrigado! :)
quarezz 15/10/2015
60

Eu acredito que você pode apenas atualizar sua estrutura de dados, então:

[tableView beginUpdates];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES];
[tableView endUpdates];

Além disso, o "withRowAnimation" não é exatamente um booleano, mas um estilo de animação:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle
Tiago Fael Matos
fonte
Coisas simples, mas tão fáceis de acessar diretamente o StackOverflow e obter o snippet necessário, do que pesquisar documentos. . Obrigado!
Jasper Blues
24

Todas essas respostas assumem que você está usando um UITableView com apenas 1 seção.

Para lidar com precisão em situações nas quais você tem mais de 1 seção, use:

NSRange range = NSMakeRange(0, myTableView.numberOfSections);
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndexesInRange:range];
[myTableView reloadSections:indexSet withRowAnimation:UITableViewRowAnimationAutomatic];

(Nota: você deve ter mais de 0 seções!)

Outro ponto a ser observado é que você pode encontrar um NSInternalInconsistencyException se tentar atualizar simultaneamente sua fonte de dados com esse código. Se for esse o caso, você pode usar uma lógica semelhante a esta:

int sectionNumber = 0; //Note that your section may be different

int nextIndex = [currentItems count]; //starting index of newly added items

[myTableView beginUpdates];

for (NSObject *item in itemsToAdd) {
    //Add the item to the data source
    [currentItems addObject:item];

    //Add the item to the table view
    NSIndexPath *path = [NSIndexPath indexPathForRow:nextIndex++ inSection:sectionNumber];
    [myTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:path] withRowAnimation:UITableViewRowAnimationAutomatic];
}

[myTableView endUpdates];
diadyne
fonte
3
Bom ponto sobre os cálculos da seção, mas eu tinha apenas uma seção e seu código teve um erro de um por um. Eu tive que mudar a primeira linha do seu código para NSRange range = NSMakeRange (0, myTableView.numberOfSections);
Danyal Aytekin
18

A maneira de abordar isso é dizer ao tableView para remover e adicionar linhas e seções com o

insertRowsAtIndexPaths:withRowAnimation:,
deleteRowsAtIndexPaths:withRowAnimation:,
insertSections:withRowAnimation:E
deleteSections:withRowAnimation:

métodos de UITableView.

Quando você chama esses métodos, a tabela anima dentro / fora dos itens solicitados e, em seguida, chama reloadData para que você possa atualizar o estado após esta animação. Esta parte é importante - se você animar tudo, mas não alterar os dados retornados pelo dataSource da tabela, as linhas aparecerão novamente depois que a animação for concluída.

Portanto, seu fluxo de aplicativos seria:

[self setTableIsInSecondState:YES];

[myTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:YES]];

Desde que os métodos dataSource da sua tabela retornem o novo conjunto correto de seções e linhas, marcando [self tableIsInSecondState](ou o que seja), isso atingirá o efeito que você está procurando.

iKenndac
fonte
16

Não posso comentar a resposta principal, mas uma implementação rápida seria:

self.tableView.reloadSections([0], with: UITableViewRowAnimation.fade)

você pode incluir quantas seções quiser atualizar no primeiro argumento para reloadSections.

Outras animações disponíveis nos documentos: https://developer.apple.com/reference/uikit/uitableviewrowanimation

fade A linha ou linhas inseridas ou excluídas desvanecem dentro ou fora da visualização da tabela.

direita A linha ou linhas inseridas deslizam da direita; a linha ou linhas excluídas deslizam para a direita.

left A linha ou linhas inseridas deslizam da esquerda; a linha ou linhas excluídas deslizam para a esquerda.

top A linha ou linhas inseridas deslizam da parte superior; a linha ou linhas excluídas deslizam para o topo.

bottom A linha ou linhas inseridas deslizam da parte inferior; a linha ou linhas excluídas deslizam para a parte inferior.

case none As linhas inseridas ou excluídas usam as animações padrão.

meio A visualização da tabela tenta manter as células novas e antigas centralizadas no espaço que ocupavam ou ocupariam. Disponível no iPhone 3.2.

automático A exibição de tabela escolhe um estilo de animação apropriado para você. (Introduzido no iOS 5.0.)

Christopher Larsen
fonte
1
Para Swift 4.2: self.tableView.reloadSections ([0], com: UITableView.RowAnimation.fade)
Reefwing
14

Versão Swift 4 para resposta @dmarnel:

tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
chengsam
fonte
9

Implementação rápida:

let range = NSMakeRange(0, self.tableView!.numberOfSections())
let indexSet = NSIndexSet(indexesInRange: range)
self.tableView!.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)
Michael Peterson
fonte
8

Para Swift 4

tableView.reloadSections([0], with: UITableView.RowAnimation.fade)
Claus
fonte
1

No meu caso, eu queria adicionar mais 10 linhas à visualização de tabela (para um tipo de funcionalidade "mostrar mais resultados") e fiz o seguinte:

  NSInteger tempNumber = self.numberOfRows;
  self.numberOfRows += 10;
  NSMutableArray *arrayOfIndexPaths = [[NSMutableArray alloc] init];
  for (NSInteger i = tempNumber; i < self.numberOfRows; i++) {
    [arrayOfIndexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
  }
  [self.tableView beginUpdates];
  [self.tableView insertRowsAtIndexPaths:arrayOfIndexPaths withRowAnimation:UITableViewRowAnimationTop];
  [self.tableView endUpdates];

Na maioria dos casos, em vez de "self.numberOfRows", você usaria normalmente a contagem da matriz de objetos para a visualização da tabela. Portanto, para garantir que essa solução funcione bem para você, "arrayOfIndexPaths" precisa ser uma matriz precisa dos caminhos de índice das linhas que estão sendo inseridas. Se a linha existir para qualquer um desses caminhos de índice, o código poderá falhar, portanto, você deve usar o método "reloadRowsAtIndexPaths: withRowAnimation:" para esses caminhos de índice para evitar falhas.

Lucas
fonte
1

Se você deseja adicionar suas próprias animações personalizadas às células UITableView, use

[theTableView reloadData];
[theTableView layoutSubviews];
NSArray* visibleViews = [theTableView visibleCells];

para obter uma matriz de células visíveis. Em seguida, adicione qualquer animação personalizada a cada célula.

Confira esta essência que eu postei para uma animação de célula personalizada suave. https://gist.github.com/floprr/1b7a58e4a18449d962bd

flopr
fonte
1

A animação sem reloadData () no Swift pode ser feita assim (a partir da versão 2.2):

tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
    // ...or here, if you need to add/remove the same amount of objects to/from somewhere
    indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
    numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()

caso precise chamar reloadData () após o término da animação, você pode adotar as alterações na transação CAT da seguinte maneira:

CATransaction.begin()
CATransaction.setCompletionBlock({() in self.tableview.reloadData() })
tableview.beginUpdates()
var indexPathsToDeleteForAnimation: [NSIndexPath] = []
var numOfCellsToRemove = ArrayOfItemsToRemove.count ?? 0

// Do your work here
while numOfCellsToRemove > 0 {
     // ...or here, if you need to add/remove the same amount of objects to/from somewhere
     indexPathsToDeleteForAnimation.append(NSIndexPath(forRow: selectedCellIndex+numOfCellsToRemove, inSection: 0))
     numOfCellsToRemove -= 1
}
tableview.deleteRowsAtIndexPaths(indexPathsToDeleteForAnimation, withRowAnimation: UITableViewRowAnimation.Right)
tableview.endUpdates()
CATransaction.commit()

A lógica é mostrada para o caso quando você exclui linhas, mas a mesma idéia também funciona para adicionar linhas. Você também pode alterar a animação para UITableViewRowAnimation.Left para torná-la elegante ou escolher na lista de outras animações disponíveis.

Vitalii
fonte
1
CATransition *animation = [CATransition animation];
animation.duration = .3;
[animation setType:kCATransitionPush];
[animation setSubtype:kCATransitionFromLeft];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[animation setDuration:.3];

[[_elementTableView layer] addAnimation:animation forKey:@"UITableViewReloadDataAnimationKey"];

[tableView reloadData];
Mayur Sardana
fonte
2
Por que definir a duração .3duas vezes?
Pang
1

Para recarregar todas as seções , não apenas uma com duração personalizada .

durationParâmetro do usuário de UIView.animatepara definir a duração personalizada.

UIView.animate(withDuration: 0.4, animations: { [weak self] in
    guard let `self` = self else { return }
    let indexSet = IndexSet(integersIn: 0..<self.tableView.numberOfSections)
    self.tableView.reloadSections(indexSet, with: UITableView.RowAnimation.fade)
})
JPetric
fonte
0

UITableViewAnimações nativas no Swift

Insira e exclua linhas de uma só vez tableView.performBatchUpdatespara que elas ocorram simultaneamente. Com base na resposta do @iKenndac, use métodos como:

  • tableView.insertSections
  • tableView.insertRows
  • tableView.deleteSections
  • tableView.deleteRows

Ex:

 tableView.performBatchUpdates({
   tableView.insertSections([0], with: .top)
 })

Isso insere uma seção na posição zero com uma animação que é carregada da parte superior. Isso executará novamente o cellForRowAtmétodo e procurará uma nova célula nessa posição. Você pode recarregar toda a exibição da tabela dessa maneira com animações específicas.

Re: a questão OP, um sinalizador condicional teria sido necessário para mostrar as células para o estado de exibição de tabela alternativa.

construir
fonte