Lassen Sie beim Ändern eine reloadData für eine UITableView animieren

183

Ich habe eine UITableView, die zwei Modi hat. Wenn wir zwischen den Modi wechseln, habe ich eine unterschiedliche Anzahl von Abschnitten und Zellen pro Abschnitt. Idealerweise würde es eine coole Animation machen, wenn der Tisch wächst oder schrumpft.

Hier ist der Code, den ich ausprobiert habe, der aber nichts bewirkt:

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

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

Irgendwelche Gedanken darüber, wie ich das machen könnte?

Vertexwahn
quelle

Antworten:

400

Eigentlich ist es sehr einfach:

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

Aus der Dokumentation :

Durch Aufrufen dieser Methode fragt die Tabellenansicht ihre Datenquelle nach neuen Zellen für die angegebenen Abschnitte. Die Tabellenansicht animiert das Einfügen neuer Zellen, während die alten Zellen animiert werden.

Dmarnel
quelle
13
Und einfach die beste Antwort auf dieser Seite!
Matthias D
40
Dies ist nicht ganz richtig, da nur der erste Abschnitt neu geladen wird. Wenn Ihre Tabelle mehrere Abschnitte hat, wird dies nicht funktionieren
Nosrettap
4
Es ist möglich, dass Sie eine Ausnahme erhalten, wenn sich keine Daten geändert haben. Siehe meine Antwort
Matej
8
@Nosrettap: Wenn Sie mehr oder alle Abschnitte Ihres tableView neu laden möchten, müssen Sie lediglich Ihr NSIndexSet um alle Abschnittsindizes erweitern, die ebenfalls aktualisiert werden müssen
JonEasy
Schnelle Version: _tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: .Fade)Insbesondere wird jedoch nur Abschnitt 0 aktualisiert, der der erste Standardabschnitt ist.
kbpontius
260

Vielleicht möchten Sie verwenden:

Ziel c

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

Schnell

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

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

Keine Probleme. : D.

Sie können auch einen der UIViewAnimationOptionTransitionsgewünschten Effekte für coolere Effekte verwenden:

  • transitionNone
  • transitionFlipFromLeft
  • transitionFlipFromRight
  • transitionCurlUp
  • transitionCurlDown
  • transitionCrossDissolve
  • transitionFlipFromTop
  • transitionFlipFromBottom
Kenn Cal
quelle
2
Dies ist nützlich, wenn der Start- / Endstatus Ihrer Tabellenansicht sehr unterschiedlich sein wird (und es komplex wäre, die Abschnitte und Zeilen zu berechnen, die hinzugefügt / entfernt werden sollen), Sie jedoch etwas weniger störendes als das nicht animierte Neuladen verwenden.
Ben Packard
1
Dies ist keine so schöne Animation wie das Neuladen der Tabellenansicht mit den integrierten Methoden, aber im Gegensatz zu der hier erwähnten höher bewerteten Methode funktioniert diese, wenn Sie mehrere Abschnitte haben.
Mark Bridges
1
Wow, nachdem ich den ganzen Rest ausprobiert habe, ist dies trotzig perfekt für mich
Lucas Goossen
1
@ MarkBridges die höher bewertete Antwort funktioniert mit mehreren Abschnitten :) -[_tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withRowAnimation:UITableViewRowAnimationFade];
Lobianco
2
@Anthony Es funktioniert nicht, wenn der Endzustand mehr / weniger Abschnitte als der Startzustand hat. Dann müssten Sie manuell verfolgen, welche Abschnitte hinzugefügt / gelöscht wurden, was ein ziemlicher Aufwand ist.
Nikolovski
78

Haben Sie mehr Freiheit mit CATransitionKlasse.

Es ist nicht auf das Verblassen beschränkt, sondern kann auch Bewegungen ausführen.


Beispielsweise:

(nicht vergessen zu importieren 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"];

Ändern Sie das type, um Ihren Bedürfnissen zu entsprechen, wie kCATransitionFadeusw.

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

Referenz für 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
quelle
Dadurch wird die gesamte Tabelle anstelle einzelner Zeilen / Abschnitte animiert.
Agos
@Agos Es beantwortet immer noch die Frage. Eine Frage "Wie lade ich nur eine Zeile neu?" Hat eine ganz andere Antwort, z. B. das Anwenden der Animation auf die layerEigenschaft von a UITableViewCell.
Matej
@matejkramny Ich habe erwartet, dass die Tabelle nur die verschiedenen Zeilen animiert (wie in der Frage angegeben), aber diese Methode pusht die gesamte Tabelle auf einmal. Vielleicht fehlt mir etwas?
Agos
@Agos hmm nein das soll es machen. Es kann nicht nur die Hälfte der Tabelle erstellt werden, da QuartzCore die Ansicht direkt ändert. Sie können versuchen, die Zellen aus der Tabellenansicht abzurufen und diese Animation dann auf jede anzuwenden (aber nicht sicher, ob es funktionieren würde)
Matej
2
hat wie ein Zauber mit kCATransitionFade funktioniert Danke! :)
Quarezz
60

Ich glaube, Sie können einfach Ihre Datenstruktur aktualisieren, dann:

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

Außerdem ist "withRowAnimation" nicht gerade ein Boolescher Wert, sondern ein Animationsstil:

UITableViewRowAnimationFade,
UITableViewRowAnimationRight,
UITableViewRowAnimationLeft,
UITableViewRowAnimationTop,
UITableViewRowAnimationBottom,
UITableViewRowAnimationNone,
UITableViewRowAnimationMiddle
Tiago Fael Matos
quelle
Einfaches Zeug, aber so einfach, direkt zu StackOverflow zu gehen und das benötigte Snippet zu erhalten, als Trawl-Dokumente. . Vielen Dank!
Jasper Blues
24

Bei all diesen Antworten wird davon ausgegangen, dass Sie eine UITableView mit nur einem Abschnitt verwenden.

Um Situationen, in denen Sie mehr als einen Abschnitt haben, genau zu behandeln, verwenden Sie:

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

(Hinweis: Sie sollten sicherstellen, dass Sie mehr als 0 Abschnitte haben!)

Beachten Sie außerdem, dass möglicherweise eine NSInternalInconsistencyException auftritt, wenn Sie versuchen, Ihre Datenquelle gleichzeitig mit diesem Code zu aktualisieren. In diesem Fall können Sie eine ähnliche Logik verwenden:

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
quelle
3
Guter Punkt zu den Abschnittsberechnungen, aber ich hatte nur einen Abschnitt und Ihr Code hatte einen Fehler nach dem anderen. Ich musste die erste Zeile Ihres Codes in NSRange range = NSMakeRange (0, myTableView.numberOfSections) ändern.
Danyal Aytekin
18

Der Weg, dies zu erreichen, besteht darin, die tableView anzuweisen, Zeilen und Abschnitte mit dem zu entfernen und hinzuzufügen

insertRowsAtIndexPaths:withRowAnimation:,
deleteRowsAtIndexPaths:withRowAnimation:,
insertSections:withRowAnimation:Und
deleteSections:withRowAnimation:

Methoden von UITableView.

Wenn Sie diese Methoden aufrufen, animiert die Tabelle die von Ihnen angeforderten Elemente ein / aus und ruft dann reloadData für sich selbst auf, damit Sie den Status nach dieser Animation aktualisieren können. Dieser Teil ist wichtig. Wenn Sie alles animieren, aber die von der dataSource der Tabelle zurückgegebenen Daten nicht ändern, werden die Zeilen nach Abschluss der Animation erneut angezeigt.

Ihr Anwendungsfluss wäre also:

[self setTableIsInSecondState:YES];

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

Solange die dataSource-Methoden Ihrer Tabelle durch Aktivieren [self tableIsInSecondState](oder was auch immer) den richtigen neuen Satz von Abschnitten und Zeilen zurückgeben, wird der gewünschte Effekt erzielt .

iKenndac
quelle
16

Ich kann die Top-Antwort nicht kommentieren, aber eine schnelle Implementierung wäre:

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

Sie können so viele Abschnitte einschließen, wie Sie im ersten Argument für reloadSections aktualisieren möchten.

Weitere Animationen finden Sie in den Dokumenten: https://developer.apple.com/reference/uikit/uitableviewrowanimation

Überblenden Die eingefügten oder gelöschten Zeilen oder Zeilen werden in die Tabellenansicht ein- oder ausgeblendet.

rechts Die eingefügte Zeile oder die eingefügten Zeilen werden von rechts eingeblendet. Die gelöschte Zeile oder die gelöschten Zeilen werden nach rechts verschoben.

left Die eingefügten Zeilen werden von links eingefügt. Die gelöschte Zeile oder die gelöschten Zeilen werden nach links verschoben.

oben Die eingefügte Zeile oder die eingefügten Zeilen werden von oben hineingeschoben. Die gelöschte Zeile oder die gelöschten Zeilen werden nach oben verschoben.

unten Die eingefügte Reihe oder Reihen werden von unten hineingeschoben. Die gelöschte Zeile oder die gelöschten Zeilen werden nach unten verschoben.

case none Die eingefügten oder gelöschten Zeilen verwenden die Standardanimationen.

Mitte Die Tabellenansicht versucht, die alten und neuen Zellen in dem Raum zu zentrieren, den sie belegt haben oder belegen werden. Verfügbar in iPhone 3.2.

automatisch Die Tabellenansicht wählt einen geeigneten Animationsstil für Sie. (Eingeführt in iOS 5.0.)

Christopher Larsen
quelle
1
Für Swift 4.2: self.tableView.reloadSections ([0], mit: UITableView.RowAnimation.fade)
Reefwing
14

Swift 4 Version für @dmarnel Antwort:

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

Schnelle Implementierung:

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

Für Swift 4

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

In meinem Fall wollte ich der Tabellenansicht 10 weitere Zeilen hinzufügen (für eine Funktionalität vom Typ "Weitere Ergebnisse anzeigen"), und ich habe Folgendes getan:

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

In den meisten Fällen verwenden Sie anstelle von "self.numberOfRows" normalerweise die Anzahl der Objektarrays für die Tabellenansicht. Um sicherzustellen, dass diese Lösung für Sie gut funktioniert, muss "arrayOfIndexPaths" ein genaues Array der Indexpfade der eingefügten Zeilen sein. Wenn die Zeile für einen dieser Indexpfade vorhanden ist, kann der Code abstürzen. Verwenden Sie daher die Methode "reloadRowsAtIndexPaths: withRowAnimation:" für diese Indexpfade, um Abstürze zu vermeiden

Lucas Chwe
quelle
1

Wenn Sie UITableView-Zellen eigene benutzerdefinierte Animationen hinzufügen möchten, verwenden Sie

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

um ein Array von sichtbaren Zellen zu erhalten. Fügen Sie dann jeder Zelle eine benutzerdefinierte Animation hinzu.

Schauen Sie sich diesen Kern an, den ich für eine reibungslose benutzerdefinierte Zellenanimation gepostet habe. https://gist.github.com/floprr/1b7a58e4a18449d962bd

flopr
quelle
1

Das Animieren ohne reloadData () in Swift kann folgendermaßen erfolgen (ab 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()

Falls Sie reloadData () nach dem Ende der Animation aufrufen müssen, können Sie die Änderungen in CATransaction folgendermaßen übernehmen:

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

Die Logik wird für den Fall angezeigt, dass Sie Zeilen löschen. Die gleiche Idee gilt jedoch auch für das Hinzufügen von Zeilen. Sie können die Animation auch in UITableViewRowAnimation.Left ändern, um sie ordentlich zu gestalten, oder aus der Liste der anderen verfügbaren Animationen auswählen.

Vitalii
quelle
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
quelle
2
Warum die Dauer .3zweimal einstellen ?
Pang
1

Zum erneuten Laden aller Abschnitte , nicht nur eines mit benutzerdefinierter Dauer .

Benutzerparameter durationvon UIView.animate, um die benutzerdefinierte Dauer festzulegen.

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
quelle
0

Native UITableViewAnimationen in Swift

Einfügen und Löschen von Zeilen auf einmal mit, tableView.performBatchUpdatesdamit sie gleichzeitig auftreten. Aufbauend auf der Antwort von @iKenndac verwenden Sie Methoden wie:

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

Ex:

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

Dadurch wird ein Abschnitt an der Nullposition mit einer Animation eingefügt, die von oben geladen wird. Dadurch wird die cellForRowAtMethode erneut ausgeführt und an dieser Position nach einer neuen Zelle gesucht. Auf diese Weise können Sie die gesamte Tabellenansicht mit bestimmten Animationen neu laden.

Betreff: Bei der OP-Frage wäre ein bedingtes Flag erforderlich gewesen, um die Zellen für den Status der alternativen Tabellenansicht anzuzeigen.

Kunst
quelle