Avoir un reloadData pour un UITableView animer lors du changement

186

J'ai un UITableView qui a deux modes. Lorsque nous basculons entre les modes, j'ai un nombre différent de sections et de cellules par section. Idéalement, cela ferait une animation sympa lorsque la table grandit ou rétrécit.

Voici le code que j'ai essayé, mais il ne fait rien:

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

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

Des pensées sur la façon dont je pourrais faire ça?

Vertexwahn
la source

Réponses:

402

En fait, c'est très simple:

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

De la documentation :

L'appel de cette méthode oblige la vue tableau à demander à sa source de données de nouvelles cellules pour les sections spécifiées. La vue tableau anime l'insertion de nouvelles cellules dans tout comme elle anime les anciennes cellules.

dmarnel
la source
13
Et tout simplement la meilleure réponse sur cette page!
Matthias D
42
Ce n'est pas tout à fait correct, car cela ne rechargera que la première section. Si votre table comporte plusieurs sections, cela ne fonctionnera pas
Nosrettap
4
Il est possible que vous obteniez une exception si aucune donnée n'a changé. Voir ma réponse
Matej
8
@Nosrettap: si vous voulez avoir plus ou toutes les sections de votre tableView recharger, tout ce que vous avez à faire est d'étendre votre NSIndexSet avec tous les index de section, qui doivent également être rafraîchis
JonEasy
Version Swift: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)Notamment, cependant, il ne met à jour que la section 0, qui est la première section par défaut.
kbpontius
272

Vous voudrez peut-être utiliser:

Objectif c

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

Rapide

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

Swift 3, 4 et 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

Pas de soucis. :RÉ

Vous pouvez également utiliser n'importe lequel des UIViewAnimationOptionTransitionseffets que vous souhaitez pour des effets plus cool:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom
Kenn Cal
la source
2
Ceci est utile si l'état de début / fin de votre tableview est très différent (et il serait complexe de calculer les sections et les lignes à ajouter / supprimer), mais que vous faites quelque chose de moins discordant que le rechargement non animé.
Ben Packard
1
Ce n'est pas une animation aussi agréable que de recharger la vue de la table à l'aide de ses méthodes intégrées, mais contrairement à la méthode la mieux notée mentionnée ici, celle-ci fonctionne lorsque vous avez plusieurs sections.
Mark Bridges
1
Wow, après avoir essayé tout le reste, c'est parfait pour moi
Lucas Goossen
1
@MarkBridges la réponse la mieux notée fonctionne avec plusieurs sections :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
lobianco
2
@Anthony Cela ne fonctionne pas si l'état final a plus / moins de sections que l'état de départ. Ensuite, vous devrez suivre manuellement les sections ajoutées / supprimées, ce qui est assez compliqué.
nikolovski
79

Ayez plus de liberté en utilisant la CATransitionclasse.

Il ne se limite pas à la décoloration, mais peut également faire des mouvements.


Par exemple:

(n'oubliez pas d'importer 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"];

Modifiez le typepour correspondre à vos besoins, par exemple, kCATransitionFadeetc.

Implémentation dans 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()

Référence pour 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
la source
cela anime le tableau dans son ensemble, au lieu de simples lignes / sections.
Agos
@Agos Il répond toujours à la question. Une question "Comment recharger une seule ligne" a une réponse tout à fait différente, telle que l'application de l'animation à la layerpropriété d'un UITableViewCell.
Matej
@matejkramny Je m'attendais à ce que le tableau n'anime que les différentes lignes (comme indiqué dans la question), mais cette méthode pousse toute la table à la fois. Peut-être que je manque quelque chose?
Agos
@Agos hmm non, c'est censé faire ça. Il ne peut pas faire seulement la moitié de la table car QuartzCore modifie la vue directement. Vous pouvez essayer d'obtenir les cellules à partir de la vue tableau, puis appliquer cette animation à chacun (mais pas sûr que cela fonctionnerait)
Matej
3
a fonctionné comme un charme avec kCATransitionFade Merci! :)
quarezz
60

Je pense que vous pouvez simplement mettre à jour votre structure de données, puis:

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

De plus, "withRowAnimation" n'est pas exactement un booléen, mais un style d'animation:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle
Tiago Fael Matos
la source
1
Des trucs simples, mais si faciles à accéder directement à StackOverflow et à obtenir l'extrait de code dont vous avez besoin, que les documents sur le chalut. . Merci!
Jasper Blues
24

Toutes ces réponses supposent que vous utilisez un UITableView avec seulement 1 section.

Pour gérer avec précision les situations où vous avez plus d'une section, utilisez:

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

(Remarque: vous devez vous assurer que vous avez plus de 0 sections!)

Une autre chose à noter est que vous pouvez rencontrer une NSInternalInconsistencyException si vous essayez de mettre à jour simultanément votre source de données avec ce code. Si tel est le cas, vous pouvez utiliser une logique similaire à celle-ci:

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
la source
3
Bon point à propos des calculs de section, mais je n'avais qu'une seule section et votre code avait une erreur de coup par coup. J'ai dû changer la première ligne de votre code en NSRange range = NSMakeRange (0, myTableView.numberOfSections);
Danyal Aytekin
18

La façon d'aborder cela est de dire à tableView de supprimer et d'ajouter des lignes et des sections avec

insertRowsAtIndexPaths:withRowAnimation:,
deleteRowsAtIndexPaths:withRowAnimation:,
insertSections:withRowAnimation:Et
deleteSections:withRowAnimation:

méthodes de UITableView.

Lorsque vous appelez ces méthodes, la table animera dans / hors les éléments que vous avez demandés, puis appellera reloadData sur elle-même afin que vous puissiez mettre à jour l'état après cette animation. Cette partie est importante - si vous animez tout mais ne modifiez pas les données renvoyées par la source de données de la table, les lignes réapparaîtront une fois l'animation terminée.

Ainsi, votre flux d'application serait:

[self setTableIsInSecondState:YES];

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

Tant que les méthodes dataSource de votre table retournent le nouvel ensemble correct de sections et de lignes en cochant [self tableIsInSecondState](ou quoi que ce soit), cela produira l'effet que vous recherchez.

iKenndac
la source
17

Je ne peux pas commenter la réponse principale, mais une mise en œuvre rapide serait:

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

vous pouvez inclure autant de sections que vous souhaitez mettre à jour dans le premier argument de reloadSections.

Autres animations disponibles dans la documentation: https://developer.apple.com/reference/uikit/uitableviewrowanimation

fondu La ou les lignes insérées ou supprimées s'affichent dans ou hors de la vue tableau.

right La ou les lignes insérées glissent à partir de la droite; la ou les lignes supprimées glissent vers la droite.

gauche La ou les lignes insérées glissent à partir de la gauche; la ou les lignes supprimées glissent vers la gauche.

top La ou les lignes insérées glissent depuis le haut; la ou les lignes supprimées glissent vers le haut.

bas La ou les lignes insérées glissent depuis le bas; la ou les lignes supprimées glissent vers le bas.

case aucun Les lignes insérées ou supprimées utilisent les animations par défaut.

middle La vue tableau tente de garder les anciennes et les nouvelles cellules centrées dans l'espace qu'elles occupaient ou occuperont. Disponible sur iPhone 3.2.

automatique La vue de table choisit un style d'animation approprié pour vous. (Introduit dans iOS 5.0.)

Christopher Larsen
la source
1
Pour Swift 4.2: self.tableView.reloadSections ([0], avec: UITableView.RowAnimation.fade)
Reefwing
15

Version Swift 4 pour la réponse @dmarnel:

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

Mise en œuvre rapide:

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

Pour Swift 4

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

Dans mon cas, je voulais ajouter 10 lignes supplémentaires dans la tableview (pour une fonctionnalité de type "afficher plus de résultats") et j'ai fait ce qui suit:

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

Dans la plupart des cas, au lieu de "self.numberOfRows", vous utiliserez généralement le nombre du tableau d'objets pour la tableview. Donc, pour vous assurer que cette solution fonctionne bien pour vous, "arrayOfIndexPaths" doit être un tableau précis des chemins d'index des lignes insérées. Si la ligne existe pour l'un de ces chemins d'index, le code peut planter, vous devez donc utiliser la méthode "reloadRowsAtIndexPaths: withRowAnimation:" pour ces chemins d'index afin d'éviter de planter

Lucas Chwe
la source
1

Si vous souhaitez ajouter vos propres animations personnalisées aux cellules UITableView, utilisez

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

pour obtenir un tableau de cellules visibles. Ajoutez ensuite une animation personnalisée à chaque cellule.

Découvrez ce que j'ai publié pour une animation de cellule personnalisée fluide. https://gist.github.com/floprr/1b7a58e4a18449d962bd

flopr
la source
1

L'animation sans reloadData () dans Swift peut être effectuée comme ceci (à partir de la version 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()

au cas où vous auriez besoin d'appeler reloadData () après la fin de l'animation, vous pouvez accepter les changements dans CATransaction comme ceci:

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 logique est affichée pour le cas où vous supprimez des lignes, mais la même idée fonctionne également pour l'ajout de lignes. Vous pouvez également modifier l'animation en UITableViewRowAnimation.Left pour la rendre nette, ou choisir dans la liste des autres animations disponibles.

Vitalii
la source
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
la source
2
Pourquoi définir la durée .3deux fois?
Pang
1

Pour recharger toutes les sections , pas seulement une avec une durée personnalisée .

durationParamètre utilisateur de UIView.animatepour définir la durée personnalisée.

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
la source
0

UITableViewAnimations natives dans Swift

Insérez et supprimez des lignes en même temps avec tableView.performBatchUpdatespour qu'elles se produisent simultanément. En vous appuyant sur la réponse de @iKenndac, utilisez des méthodes telles que:

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

Ex:

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

Cela insère une section à la position zéro avec une animation qui se charge du haut. Cela réexécutera lecellForRowAt méthode et recherchera une nouvelle cellule à cette position. Vous pouvez recharger la vue de table entière de cette façon avec des animations spécifiques.

Re: la question OP, un drapeau conditionnel aurait été nécessaire pour afficher les cellules pour l'état de vue de table alternatif.

artisanat
la source