Avere un reloadData per un UITableView animato quando si cambia


183

Ho un UITableView che ha due modalità. Quando si passa da una modalità all'altra ho un numero diverso di sezioni e celle per sezione. Idealmente, farebbe qualche bella animazione quando la tabella cresce o si restringe.

Ecco il codice che ho provato, ma non fa nulla:

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

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

Qualche idea su come potrei farlo?

Risposte:


400

In realtà, è molto semplice:

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

Dalla documentazione :

La chiamata a questo metodo fa sì che la vista tabella richieda all'origine dati nuove celle per le sezioni specificate. La vista tabella anima l'inserimento di nuove celle mentre anima le vecchie celle.


13
E semplicemente la migliore risposta in questa pagina!
Matthias D,

41
Questo non è del tutto corretto, perché ricaricherà solo la prima sezione. Se il tuo tavolo ha più sezioni, questo non funzionerà
Nosrettap,

4
È possibile che otterrai un'eccezione se non sono stati modificati dati. Vedi la mia risposta
Matej

8
@Nosrettap: se vuoi avere più o tutte le sezioni del tuo tableView ricaricare, tutto ciò che devi fare è estendere il tuo NSIndexSet con tutti gli indici di sezione, che deve essere aggiornato anche
JonEasy

Versione rapida: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)In particolare, tuttavia, aggiorna solo la Sezione 0, che è la prima sezione predefinita.
kbpontius

264

Potresti voler usare:

Objective-C

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

veloce

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

Nessun problema. : D

Puoi anche utilizzare uno qualsiasi degli UIViewAnimationOptionTransitionseffetti desiderati per ottenere effetti più interessanti:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom

2
Ciò è utile se lo stato iniziale / finale della visualizzazione della tabella sarà molto diverso (e sarebbe complesso calcolare le sezioni e le righe da aggiungere / rimuovere), ma si bacchetta qualcosa di meno stonato del ricaricare non animato.
Ben Packard,

1
Questa non è un'animazione piacevole come ricaricare la vista tabella usando i suoi metodi integrati, ma a differenza del metodo con rating più alto menzionato qui, questo funziona quando hai più sezioni.
Mark Bridges il

1
Wow dopo aver provato tutto il resto, questo è provocatoriamente perfetto per me
Lucas Goossen,

1
@MarkBridges la risposta più votata funziona con più sezioni :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
lobianco

2
@Anthony Non funziona se lo stato finale ha più / meno sezioni rispetto allo stato iniziale. Quindi dovresti tracciare manualmente quali sezioni sono state aggiunte / eliminate, il che è piuttosto una seccatura.
nikolovski,

78

Avere più libertà usando la CATransitionclasse.

Non si limita allo sbiadimento, ma può fare anche movimenti.


Per esempio:

(non dimenticare di importare 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"];

Cambia typein base alle tue esigenze, come kCATransitionFadeecc.

Implementazione in 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()

Riferimento per 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()

questo anima la tabella nel suo insieme, anziché singole righe / sezioni.
Agos,

@Agos Risponde ancora alla domanda. Una domanda "Come ricaricare solo una riga" ha una risposta alquanto diversa, come applicare l'animazione alla layerproprietà di a UITableViewCell.
Matej,

@matejkramny Mi aspettavo che la tabella animasse solo le diverse righe (come indicato nella domanda), ma questo metodo spinge tutta la tabella in una volta. Forse mi manca qualcosa?
Agos,

@Agos hmm no, dovrebbe farlo. Non può fare solo metà della tabella poiché QuartzCore modifica direttamente la vista. Puoi provare a ottenere le celle dalla vista della tabella e quindi applicare questa animazione a ciascuno di essi (ma non sono sicuro che funzionerebbe)
Matej

2
ha funzionato come un incantesimo con kCATransitionFade Grazie! :)
quarezz,

60

Credo che puoi semplicemente aggiornare la struttura dei tuoi dati, quindi:

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

Inoltre, "withRowAnimation" non è esattamente un valore booleano, ma uno stile di animazione:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle

Roba semplice, ma così facile andare direttamente su StackOverflow e ottenere lo snippet di cui hai bisogno, rispetto ai documenti a strascico. . Grazie!
Jasper Blues,

24

Tutte queste risposte presuppongono che tu stia utilizzando un UITableView con solo 1 sezione.

Per gestire con precisione situazioni in cui hai più di una sezione utilizzare:

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

(Nota: dovresti assicurarti di avere più di 0 sezioni!)

Un'altra cosa da notare è che è possibile imbattersi in NSInternalInconsistencyException se si tenta di aggiornare contemporaneamente l'origine dati con questo codice. In tal caso, è possibile utilizzare una logica simile a questa:

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

3
Un buon punto sui calcoli della sezione, ma avevo solo una sezione e il tuo codice aveva un errore off-by-one. Ho dovuto cambiare la prima riga del codice in NSRange range = NSMakeRange (0, myTableView.numberOfSections);
Danyal Aytekin,

18

Il modo per accedervi è dire a tableView di rimuovere e aggiungere righe e sezioni con il

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

metodi di UITableView.

Quando si chiamano questi metodi, la tabella animerà dentro / fuori gli elementi richiesti, quindi chiamerà reloadData su se stessa in modo da poter aggiornare lo stato dopo questa animazione. Questa parte è importante: se si anima tutto ma non si modificano i dati restituiti dall'origine dati della tabella, le righe verranno visualizzate di nuovo al termine dell'animazione.

Quindi, il flusso dell'applicazione sarebbe:

[self setTableIsInSecondState:YES];

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

Finché i metodi DataSource della tabella restituiscono il nuovo set corretto di sezioni e righe selezionando [self tableIsInSecondState](o qualsiasi altra cosa), ciò otterrà l'effetto che stai cercando.


16

Non posso commentare la risposta migliore, ma un'implementazione rapida sarebbe:

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

potresti includere tutte le sezioni che vuoi aggiornare nel primo argomento per reloadSections.

Altre animazioni disponibili dai documenti: https://developer.apple.com/reference/uikit/uitableviewrowanimation

dissolvenza La riga o le righe inserite o eliminate si dissolvono all'interno o all'esterno della vista tabella.

right La riga o le righe inserite scorrono da destra; la riga o le righe eliminate scivolano verso destra.

left La riga o le righe inserite scorrono da sinistra; la riga o le righe eliminate scivolano verso sinistra.

top La riga o le righe inserite scorrono dall'alto; la riga o le righe eliminate scivolano verso l'alto.

bottom La riga o le righe inserite scorrono dal basso; la riga o le righe eliminate scivolano verso il basso.

case none Le righe inserite o eliminate utilizzano le animazioni predefinite.

al centro La vista tabella tenta di mantenere centrate le celle vecchie e nuove nello spazio che occupavano o occuperanno. Disponibile in iPhone 3.2.

automatico La vista tabella sceglie uno stile di animazione appropriato per te. (Introdotto in iOS 5.0.)


1
Per Swift 4.2: self.tableView.reloadSections ([0], con: UITableView.RowAnimation.fade)
Reefwing

14

Versione Swift 4 per la risposta di @dmarnel:

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

9

Implementazione rapida:

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

8

Per Swift 4

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

1

Nel mio caso, volevo aggiungere altre 10 righe nella vista tabella (per un tipo di funzionalità "mostra più risultati") e ho fatto quanto segue:

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

Nella maggior parte dei casi, anziché "self.numberOfRows", in genere si utilizza il conteggio della matrice di oggetti per la vista tabella. Quindi, per essere sicuro che questa soluzione funzioni bene per te, "arrayOfIndexPaths" deve essere un array accurato dei percorsi dell'indice delle righe da inserire. Se la riga esiste per uno di questi percorsi dell'indice, il codice potrebbe bloccarsi, quindi è necessario utilizzare il metodo "reloadRowsAtIndexPaths: withRowAnimation:" per quei percorsi dell'indice per evitare arresti anomali


1

Se si desidera aggiungere animazioni personalizzate alle celle UITableView, utilizzare

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

per ottenere una matrice di celle visibili. Quindi aggiungi qualsiasi animazione personalizzata a ogni cella.

Dai un'occhiata a questa idea che ho pubblicato per un'animazione cellulare personalizzata fluida. https://gist.github.com/floprr/1b7a58e4a18449d962bd


1

L'animazione senza reloadData () in Swift può essere eseguita in questo modo (dalla versione 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()

nel caso in cui sia necessario chiamare reloadData () al termine dell'animazione, è possibile accettare le modifiche in CATransaction in questo modo:

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

La logica viene mostrata per il caso in cui si eliminano le righe, ma la stessa idea funziona anche per l'aggiunta di righe. Puoi anche cambiare l'animazione in UITableViewRowAnimation.Left per renderla pulita o scegliere dall'elenco di altre animazioni disponibili.


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

2
Perché impostare la durata .3due volte?
Provò il

1

Per ricaricare tutte le sezioni , non solo una con durata personalizzata .

durationParametro utente di UIView.animateper impostare la durata personalizzata.

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

0

UITableViewAnimazioni native in Swift

Inserisci ed elimina le righe contemporaneamente con in tableView.performBatchUpdatesmodo che si verifichino contemporaneamente. Sulla base della risposta di @iKenndac, utilizzare metodi come:

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

Ex:

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

Ciò inserisce una sezione nella posizione zero con un'animazione che si carica dall'alto. Ciò eseguirà nuovamente il cellForRowAtmetodo e verificherà una nuova cella in quella posizione. È possibile ricaricare l'intera vista della tabella in questo modo con animazioni specifiche.

Ri: la domanda OP, sarebbe stato necessario un flag condizionale per mostrare le celle per lo stato di visualizzazione della tabella alternativa.

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.