UITableView leeren

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • UITableView leeren

    Hallo,

    in meiner App habe ich einen ViewController in dem u.a. auch eine UITabelView ist. Bei bestimmten Aktionen wird eine andere Klasse (kein ViewController) aufgerufen und dabei wird teilweise die Liste mit ganz anderen Werten gefüllt. (Mehr oder weniger Spalten, Sektionen, Zeilenhöhe etc.)

    Wenn ich nun zurückkomme in den ViewController, zeigt er mir zunächst keine Änderung, bis ich scrolle, oder drehe und dann werden die neuen Inhalte in der Liste angezeigt.
    Das ist auch soweit klar und verständlich.

    Ich habe dann die Methode reloadData ausprobiert und das sah schon besser aus, aber immer noch waren Teile der alten Liste zu finden und das Layout hat nicht ganz gepasst.

    Gibt es eine Methode, um die Liste komplett zu leeren?
    Ich habe mir deleteRowsAtIndexPath und deleteSections angesehen und auch versucht, beide einmal einzusetzen. Aber entweder brachte es nichts oder ich beklam eine Fehlermeldung, dass nach dem Aktualisieren die Anzahl der Elemente nicht mehr konsistent sei. (Der Text war etwas anders fprmuliert, aber ihr wisst sicherlich, was ich meine).

    Hat jemand von euch einern Ansatz zum vollständigen Leeren der TableView?
  • Wenn reloadData nicht funktioniert dann ist da ganz was anderes im Argen. Als erstes wuerde ich mal Schaum ob mein tableview Pointer überhaupt stimmt. Als zweites würde ich mir mal die neue liste ausgeben lassen ob die überhaupt konsistent ist. (was ist überhaupt eine liste unter iOS? Ich kenne nur array,Dictionary und Sets)

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Hallo,

    Ich habe mir deleteRowsAtIndexPath und deleteSections angesehen und auch versucht, beide einmal einzusetzen.


    Ich glaube, hier liegt ein Missverständnis vor.

    Du löschst keine Zeilen, sondern den darunterlegenden Eintrag.
    D.h. wenn der Anwender Zeile 5 löschen möchte, dann musst Du in deinem Array oder was sich immer den fünften Eintrag löschen.

    Das löschen der Daten erfolgt somit nicht direkt über die Tabelle, sondern via der Datenquelle.

    Viele Grüße
  • Erst mal danke für eure Hilfestellung!

    Nachdem ich nun noch dies und das umgestellt habe, sieht es folgendermaßen aus:
    Wenn ich die TableView das 2. Mal fülle, bleiben Teile der alten stehen.
    Das liegt z.B. daran, dass in der ersten TableView u.a. auch Images angezeigt wurden.
    Beim Füllen der Anzeige (cellForRowAtIndexPath) werden diese UIImages entsprechend erzeugt.

    Wird die TableView nun mit anderen Daten gefüllt, so werden zwar die Label gefunden und recycled, aber die Images bleiben unangetastet und werden daher weiter angezeigt.
    Wenn ich das iPhone drehe, suche ich nach Zellen mit einem anderen Namen (über dequeueReusableCellWithIdentifier). Beim ersten Drehen gibt es solche Zellen noch nicht und die TableView wird dann auch korrekt angezeigt.
    Im weiteren Verlauf verschlimmert sich dann alles... ;(

    Ich habe dann einen Button eingebaut, der ein reloadData aufruft, aber das ändert nichts.
    Ich müsste vielmehr alle Label und Images, die ich in die TableView gesteckt habe, wieder entfernen. An anderer Stelle (bei einer reinen Dialoganzeige mit TExtEdit und Labeln mache ich es so:

    Quellcode

    1. -(void)emptyView:(UIView*)view {
    2. for (UIView *sView in [view subviews]) {
    3. [sView removeFromSuperview];
    4. }
    5. }


    Auf die TableView angewandt, zeigt das aber keinen Erfolg.

    Dann habe ich beim Füllen der Zellen zunächst alle subViews versteckt:

    Quellcode

    1. for (UIView * view in [cell.contentView subviews]) {
    2. view.hidden = YES;
    3. }


    Anschließend suche ich anhand meiner Daten nach den Labeln und Images mit [cell.contentView viewWithTag:tag] und mache diese Objekte dann wieder sichtbar.

    Das funktioniert ! :D

    Aber es gefällt mir nicht ! :S
    Denn jetzt ändere ich permanent die Sichtbarkeit, auch wenn ich in derselben TableView bleibe und nur scrolle und das ist nicht sehr elegant.
    Lieber wäre mir, ich könnte beim Wechsel ein einziges Mal für die Bereinigung sorgen und dann "normal" weiter arbeiten.

    @Claus:
    Ich habe nicht ein einziges Mal "Liste" gesagt, oder? ;)
  • So klappt es:

    Quellcode

    1. -(void)emptyList {
    2. DLog(@"Leere Liste...");
    3. int i, j;
    4. NSIndexPath *indexPath;
    5. for (i = 0; i < [vController.viewListe numberOfSections]; i++) {
    6. for (j = 0; j < [vController.viewListe numberOfRowsInSection:i]; j++) {
    7. indexPath = [NSIndexPath indexPathForRow:j inSection:i];
    8. UITableViewCell *cell = [vController.viewListe cellForRowAtIndexPath:indexPath];
    9. for (UIView * view in [cell.contentView subviews]) {
    10. [view removeFromSuperview];
    11. }
    12. }
    13. }
    14. }
    Alles anzeigen


    Nochmals Dank für die Hilfe und wenn es noch Verbesserungsvorschläge gibt: Gerne !
  • Also was du da machst ist vollkommen von hinten durch die Brust ins Auge und wird bei einer längeren Tabelle ruckeln wie Sau.

    Lies dir unbedingt einmal die Doku zum tableview durch

    developer.apple.com/library/io…/Reference/Reference.html

    Und verstehe was es mit dem Reuse der cells auf sich hat. Das ist absolut elementar Und ohne das wirst du niemals eine saubere performante tableview programmieren.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Es gibt zwei Alternativen:
    1. Ihr versteht mein Problem nicht
    2. Ich seh den Wald vor lauter Bäumen nicht.

    Zu 1 noch einmal in Kurzform das Problem:
    Klar nutze ich das Recyceln der Zellen!

    Wie überall beschrieben, suche ich nach einer verfügbaren Zelle und verwende sie erneut, d.h. ich fülle Label und Images mit dem Inhalt der aktuellen Zeile.
    Falls es keine Zelle gibt, erzeuge ich eine und lege alle Objekte darin ab, die ich für die Darstellung benötige.

    ABER:
    Irgendwann ändert sich der Inhalt und dann habe ich z.B. nur noch ein Label statt zweien und das ist aber mit einer größeren Schrift, oder ich hatte vorher in der 3. Spalte ein Image und nun nur noch Text.
    Wenn ich bestehenden Zellen wiederverwende, sind dort noch die nach dem Wechsel nicht mehr benötigten und sogar störenden Objekte enthalten, da sie ja ebenfalls "reused" werden!
    Daher leere ich ein beim Wechsel des Inhaltes einziges Mal die TableView komplett.

    Zu 2:
    Wie würdet ihr das machen?

    Gruß
    Udo
  • Gibbsnich schrieb:

    Es gibt zwei Alternativen:
    1. Ihr versteht mein Problem nicht
    2. Ich seh den Wald vor lauter Bäumen nicht.

    Zu 1 noch einmal in Kurzform das Problem:
    Klar nutze ich das Recyceln der Zellen!

    Wie überall beschrieben, suche ich nach einer verfügbaren Zelle und verwende sie erneut, d.h. ich fülle Label und Images mit dem Inhalt der aktuellen Zeile.
    Falls es keine Zelle gibt, erzeuge ich eine und lege alle Objekte darin ab, die ich für die Darstellung benötige.

    ABER:
    Irgendwann ändert sich der Inhalt und dann habe ich z.B. nur noch ein Label statt zweien und das ist aber mit einer größeren Schrift, oder ich hatte vorher in der 3. Spalte ein Image und nun nur noch Text.
    Wenn ich bestehenden Zellen wiederverwende, sind dort noch die nach dem Wechsel nicht mehr benötigten und sogar störenden Objekte enthalten, da sie ja ebenfalls "reused" werden!
    Daher leere ich ein beim Wechsel des Inhaltes einziges Mal die TableView komplett.

    Zu 2:
    Wie würdet ihr das machen?

    Gruß
    Udo


    Dein gesamtes Problem liegt darin das du das Reuse einfach nicht verstehst. Reuse bedeutet die Zelle bekommt einen Identifier und immer wenn genau so eine Zelle wieder gebraucht wird, dann kannst du sie anhand des identifiers anfordern?
    Wenn nun, wie in deinem fall sich der aufbaue der Zelle aendert und sei es nur eine fontgroesse oder eine Farbe, dann musst du entweder

    1) eine neue Zelle mit einem neuen Identifier erstellen

    Oder was häufiger gemacht wird

    2) das erzeugen der sich ändernden teile der Zelle in aus dem Teil herausnehmen wo die Zelle erzeugt wird und in den Teil packen wo die zelle reused wird

    Aber genau das wirstdu nicht so verstehen was ich da gerade schreibe weil du das Prinzip nicht verstanden hast.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Thallius schrieb:

    Dein gesamtes Problem liegt darin das du das Reuse einfach nicht verstehst. Reuse bedeutet die Zelle bekommt einen Identifier und immer wenn genau so eine Zelle wieder gebraucht wird, dann kannst du sie anhand des identifiers anfordern?
    Verständnisdefizite unterstellst du etwas vorschnell. Lies mein letzten Eintrag und vergleiche meine Darstellung des Recycelns mit deiner. Wie kommst du darauf, dass ich das nicht verstanden hätte?

    Thallius schrieb:

    Wenn nun, wie in deinem fall sich der aufbaue der Zelle aendert und sei es nur eine fontgroesse oder eine Farbe, dann musst du entweder

    1) eine neue Zelle mit einem neuen Identifier erstellen
    Daran habe ich auch schon gedacht. Aber das bedeutet bei einer längeren Nutzung, dass bei jeder neuen Liste neue Zellen angelegt werden. Zwar ist die Menge pro Liste durch das Wiederverwenden übersichtich klein, aber was passiert mit den alten Zellen? Ich wollte hier vermeiden, dass sich Datenmüll aufbaut und Zellen "reusen".

    Thallius schrieb:

    Oder was häufiger gemacht wird

    2) das erzeugen der sich ändernden teile der Zelle in aus dem Teil herausnehmen wo die Zelle erzeugt wird und in den Teil packen wo die zelle reused wird
    Da sich jedes Objekt ändern kann, müsste ich stets alles neu erstellen. Was ist dann mit dem "reuse"? Dann ist die Wiederverwendung obsolet.

    Thallius schrieb:

    Aber genau das wirstdu nicht so verstehen was ich da gerade schreibe weil du das Prinzip nicht verstanden hast.
    Siehe oben.
  • Wenn du wirklich in jeder Zelle inmer wechselnde Inhalte hast, dann würde ich mir mal das konzept noch einmal genauer ansehen. Ich stelle mir das für den User verwirrend und schwer zu erkennen vor wenn es ueberhaupt kein einheitliches Konzept für die Zellen gibt.

    Und wenn du dann weist was du gleich lassen kannst u d was nicht, dann kannst du anfangen zu überlegen wie viele zelltypen du anlegen willst und Waseda dynamisch für jede zelle erstellen willst.

    Wenn du dabei bleibst das jede Zelle total anders aussehen kann wie die andere und davon auch noch beliebig viele, dann kannst du den reuse hält vergessen

    Trotzdem gibt es absolut keinen Grund von Hand views aus dem tableview zu werfen um diese zu löschen, wenn man das Reuse verstanden hat. Deshalbmbelibe ich dabei das du es nicht verstanden hast.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Hallo, ohne jetzt alles andere gelesen zu haben (nur deinen ersten Startbeitrag).

    ich gehe mal davon aus, dass Du einen TableviewController verwendest.
    Wenn Du bei diesem reloadData durchführst, werden alle Zeilen neu aufgebaut. Es wird sozusagen eine Löschung aller Zeilen vorher durchgeführt.



    Das bedeutet, wenn bei Dir in diesem Fall noch alte Daten angezeigt werden, dann mußt Du prüfen, ob warum die Datenquelle deiner Tabelle an diesen
    Stellen noch die alten Daten enthalten.
  • zweimenschen schrieb:

    Hallo, ohne jetzt alles andere gelesen zu haben (nur deinen ersten Startbeitrag).

    ich gehe mal davon aus, dass Du einen TableviewController verwendest.
    Wenn Du bei diesem reloadData durchführst, werden alle Zeilen neu aufgebaut. Es wird sozusagen eine Löschung aller Zeilen vorher durchgeführt.



    Das bedeutet, wenn bei Dir in diesem Fall noch alte Daten angezeigt werden, dann mußt Du prüfen, ob warum die Datenquelle deiner Tabelle an diesen
    Stellen noch die alten Daten enthalten.
    Ich nutze einen UIViewController, der u.a. die Protokolle UITableViewDataSource und UITableViewDelegate implementiert. Dieser Controller muss noch andere Views aufnehmen und mit dem UITableViewController ging das damals nicht. Das reloadData ist ja eine Methode vom UITableView und das habe ich auch ausprobiert. Danach kommen auch folgende Aktionen:
    1. Anzahl Zeilen werden abgerufen (numberOfRowsInSection)
    2. 6-mal wird die Höhe abgerufen (heightForRowAtIndexPath)
    3. Dann werden die Zellen gesucht: (cellForRowAtIndexPath)
    Aber diese Zellen sind noch vorhanden und werden daher erneut verwendet!

    Thallius schrieb:

    Wenn du wirklich in jeder Zelle inmer wechselnde Inhalte hast, dann würde ich mir mal das konzept noch einmal genauer ansehen. Ich stelle mir das für den User verwirrend und schwer zu erkennen vor wenn es ueberhaupt kein einheitliches Konzept für die Zellen gibt.

    Und wenn du dann weist was du gleich lassen kannst u d was nicht, dann kannst du anfangen zu überlegen wie viele zelltypen du anlegen willst und Waseda dynamisch für jede zelle erstellen willst.

    Wenn du dabei bleibst das jede Zelle total anders aussehen kann wie die andere und davon auch noch beliebig viele, dann kannst du den reuse hält vergessen

    Trotzdem gibt es absolut keinen Grund von Hand views aus dem tableview zu werfen um diese zu löschen, wenn man das Reuse verstanden hat. Deshalbmbelibe ich dabei das du es nicht verstanden hast.

    Gruß

    Claus
    Da reden wir deutlich aneinander vorbei:
    Ich rede nicht von Aktivitäten beim Betrachten und Scrollen der TableView, sondern vom Dialogwechsel!
    Stelle dir das so vor:
    1. Du hast eine Liste deiner Kunden und wählst einen aus.
    2. Die App sendet das zum Server und erhält die Daten mit den Aufträgen des Kunden.
    3. Ich parse diese Daten und in diesem Moment baue ich die TableView um und zeige nun Aufträge statt Kunden.

    Innerhalb eines Dialoges ist die Liste natürlich homogen und es wird nicht von mir gelöscht!!
    Es ist wie das Wechseln von einem Dialog zum anderen, nur dass hier alles in einem Dialog stattfindet.

    Vorher hatte ich den ViewController immer entfernt und neu erzeugt, da war die Darstellung kein Problem.

    Ich habe beim Vorbereiten der nächsten Liste einmal folgendes versucht:

    Quellcode

    1. -(void)deleteCellWithName:(NSString*)cellName {
    2. UITableViewCell *cell = nil;
    3. do {
    4. cell = [vController.viewListe dequeueReusableCellWithIdentifier:cellName];
    5. if (cell != nil) {
    6. for (UIView * view in [cell.contentView subviews]) {
    7. [view removeFromSuperview];
    8. DLog(@"remove view....");
    9. }
    10. [cell release];
    11. }
    12. } while (cell != nil);
    13. }
    Alles anzeigen


    Damit würde ich ja jeweils nur ca. 10 Zellen finden, die ich entfernen müsste, egal wie viele Elemente in der TableView angezeigt wurden.
    Leider bekomme ich da irgendwann einen EXC_BAD_ACCESS und suche noch nach der Ursache.

    Sieht von euch jemand den Fehler?

    Gruß
    Udo
  • wenn ich mir das so durchlese, kann es sein, dass du im gleichen TableView 2 verschiedene Sachen anzeigen lassen willst, einmal die Liste der Kunden und beim Auswählen eines Kunden, die Aufträge von diesem Kunden? Müsstest du da nicht einfach nur die DataSource des TableViews ändern auf die neue DataSource, nämlich die Aufträge, und dann ein Refresh des TableViews?
    [window close]
  • 3. Dann werden die Zellen gesucht: (cellForRowAtIndexPath)
    Aber diese Zellen sind noch vorhanden und werden daher erneut verwendet!

    Genau das ist doch das was du nicht versmtanden hast

    In cellforrow werden die Zellen nicht gesucht sondern erzeugt. Das ist eine Datasource Methode. Du entscheidest selber in dieser methode ob du eine neue Zelle erzeugst oder eine alte reused

    In deinem fall wuerdest du genau in dieser Methode entweder eine kundenzelle oder eine auftragszelle anfordern zum wiederbenutzen oder eben eine erzeugen wenn es noch keine gibt.

    Schon sind alle deine probleme gelöst. Aber um diesen Code so richtig zu schreiben must du hält erstmal verstehen was da überhaupt passiert in so einer tableview

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • uniique schrieb:

    wenn ich mir das so durchlese, kann es sein, dass du im gleichen TableView 2 verschiedene Sachen anzeigen lassen willst, einmal die Liste der Kunden und beim Auswählen eines Kunden, die Aufträge von diesem Kunden? Müsstest du da nicht einfach nur die DataSource des TableViews ändern auf die neue DataSource, nämlich die Aufträge, und dann ein Refresh des TableViews?
    Richtig, ich will x-mal Daten anzeigen. Meine App ist nur das Frontend und hat keine Information über die Menge und Inhalt kommender Listen.
    Die Daten stehen korrekt an und werden prinzipiell auch richtig angezeigt.
    Wenn ich aber "on the fly" nur die Daten ändere und weiter mit denselben Zellbezeichnern weitermache, nutze ich vorhandene Label und damit z.B. deren Formatierung.

    Klar kann ich bei jedem neuen Inhalt den Bezeichner der Zelle ändern, dann werden neue Zellen erzeugt und alles passt.
    Was mir aber nicht gefällt, ist die Tatsache, dass ich so Datenmüll aufhäufe, denn die alten Zellen bleiben doch im Arbeitsspeicher.

    Also möchte ich die TableView beim erneuten Bestücken qasi auf Null stellen und sauber weitermachen.

    Gruß
    Udo
  • Du verstehst nicht, dass die sichtbare Tabelle nichts mit der Haltung der Informationen gemeinsam hat.

    Also möchte ich die TableView beim erneuten Bestücken qasi auf Null stellen und sauber weitermachen.

    Was Du hier machst ist absolut falsch und einfach nur Pfusch.

    Schaue Dir mal an: UITableViewDataSource

    Ich verstehe nicht, wie man die hier gegebenen Informationen so ignorieren kann.

    UITableView leeren

    Gibt es eine Methode, um die Liste komplett zu leeren?

    Deine Frage ist einfach und die Umsetzung gleichermaßen.

    Du solltest, bevor Du mit Deinem Projekt weitermachst, ein Tutorial dazu durcharbeiten.

    Viele Grüße