numberOfRowsInSection liefert nach delete falschen Wert

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

  • numberOfRowsInSection liefert nach delete falschen Wert

    Hallo,
    was muss man denn alles bei der Verwendung von einem NSFetchedResultsController
    in Verbindung mit predicates und Tableviews beachten ?
    Verwende einen NSFetchedResultsController mit veränderbarem predicate.
    Suche und Anzeige in der TableView klappen auch bestens !
    Nur nach dem Löschen eines Satzes und anschliesendem

    Quellcode

    1. [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

    tritt folgender Fehler auf: Invalid update: invalid number of rows in section 0.....
    Debuggen ergab, dass numberOfRowsInSection leider noch den alten Wert zurückgibt !
    Wie kann man denn einen Refresh von numberOfRowsInSection am sinnvollsten erzwingen ?
    Wie handelt man denn NSFetchedResultsController mit predicates und Tableviews richtig ?

    Über einen Tipp (gerne auch einen Link zu einer guten Doku) würd ich mich sehr freuen.

    Thanks
    Matthias
  • deleteRowsAtIndexPaths:withRowAnimation: sorgt nur für eine Animation beim Löschen einer Zeile aus der TableView. Für die Entfernung der Zeile aus dem Daten-Model musst Du selbst sorgen.

    Per objectAtIndexPath: von NSFetchedResultsController kommst Du an das entsprechende Managed Objekt der zu löschenden Zeile. Über den NSManagedObjectContext kannst Du das Objekt dann per deleteObject: löschen.

    Der richtige Weg ist jedoch, das Managed Objekt zuerst über den NSManagedObjectContext zu löschen. Der NSFetchedResultsController schickt dann entsprechende Nachrichten an seinen Delegate (developer.apple.com/library/io…/Reference/Reference.html), damit man den TableView entsprechend aktualisieren kann.
  • Hi Dan,
    da habe ich wohl zu wenig Infos mitgeschickt -
    ich verwende schon die NSFetchedResultsController Variante ..
    und dennoch liefert numberOfRowsInSection falsche Werte.
    der Indexpath wird doch vom NSFetchedResultsController verwaltet
    auch wenn ein Predicate gesetzt wurde ?

    Brainfuck-Quellcode

    1. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    2. NSInteger count = [[fetchedResCtrl_DVDListen sections] count];
    3. if (count == 0) {
    4. count = 1;}
    5. return count;
    6. }
    7. //--------------------------------------------------------------------------------------------------
    8. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    9. NSInteger numberOfRows = 0;
    10. if ([[fetchedResCtrl_DVDListen sections] count] > 0) {
    11. id sectionInfo = [[fetchedResCtrl_DVDListen sections] objectAtIndex:section];
    12. numberOfRows = [sectionInfo numberOfObjects];
    13. }
    14. return numberOfRows;
    15. }
    16. //--------------------------------------------------------------------------------------------------
    17. - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    18. [listenTableView beginUpdates];
    19. }
    20. //--------------------------------------------------------------------------------------------------
    21. - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
    22. atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
    23. newIndexPath:(NSIndexPath *)newIndexPath {
    24. switch(type) {
    25. case NSFetchedResultsChangeInsert:
    26. [listenTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
    27. withRowAnimation:UITableViewRowAnimationFade];
    28. break;
    29. case NSFetchedResultsChangeDelete:
    30. [listenTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
    31. withRowAnimation:UITableViewRowAnimationFade];
    32. break;
    33. case NSFetchedResultsChangeUpdate:
    34. [self configureCell:(FilmeTableViewCell *)[listenTableView cellForRowAtIndexPath:indexPath]
    35. atIndexPath:indexPath];
    36. break;
    37. case NSFetchedResultsChangeMove:
    38. [listenTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
    39. withRowAnimation:UITableViewRowAnimationFade];
    40. [listenTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
    41. withRowAnimation:UITableViewRowAnimationFade];
    42. break;
    43. }
    44. }
    45. //--------------------------------------------------------------------------------------------------
    46. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    47. [listenTableView endUpdates]; }
    Alles anzeigen


    :?: :?: :?:
  • Uups,
    für die die in die selbe Falle getappt sind - !! WICHTIG !! vor einem neuen performfetch
    das Delegate des alten fetchedResultController auf nil setzen nicht vergessen !
    Sonst gibt konkurrierende Zugriffe !

    if (fetchedResultController) {
    self.fetchedResultController.delegate = nil;
    }
  • mackasper schrieb:

    Ich lass die TableView die Arbeit für mich machen und
    rufe dann
    context deleteObject:
    auf

    Das ist das Problem, nicht der delegate. Du musst das Objekt löschen bevor der Table View sich updated, damit dann die Daten bereits auf neuestem Stand sind. Also vom Ablauf her (Pseudocode):

    Quellcode

    1. - (void) tableView:commitEditingStyle:forRowAtIndexPath:
    2. {
    3. if (UITableViewCellEditingStyleDelete) {
    4. id delObj = [self objectAtIndexPath:<indexPath>];
    5. [controller deleteObject:delObj];
    6. // den context evtl. speichern
    7. }
    8. }


    In der Methode controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: kannst du danach die tabellenzeile löschen und der table controller wird nicht mehr meckern.

    Edit: Fehler korrigiert wie nachfolgend von McDan entdeckt.
    Widgetschmie.de • Life is too short for gadgets

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Pascal ()

  • Pascal schrieb:

    mackasper schrieb:

    Ich lass die TableView die Arbeit für mich machen und
    rufe dann
    context deleteObject:
    auf

    Das ist das Problem, nicht der delegate. Du musst das Objekt löschen bevor der Table View sich updated, damit dann die Daten bereits auf neuestem Stand sind. Also vom Ablauf her (Pseudocode):

    Quellcode

    1. - (void) tableView:commitEditingStyle:forRowAtIndexPath:
    2. {
    3. if (UITableViewCellEditingStyleDelete) {
    4. id delObj = [self objectAtIndexPath:<indexPath>];
    5. [controller deleteObject:delObj];
    6. // den context evtl. speichern
    7. [tableView deleteRowsAtIndexPaths:withRowAnimation:];
    8. }
    9. }

    Ich glaube

    Quellcode

    1. [tableView deleteRowsAtIndexPaths:withRowAnimation:];

    sollte man an dieser Stelle nicht aufrufen, da dies durch den delegate des NSFetchedResultsController getriggert wird.

    Wenn im NSFetchedResultsController natürlich kein delegate gesetzt ist, dann macht die o.a. Zeile natürlich Sinn, damit der TableView entsprechend aktualisiert wird.
  • Hmmm, also als delegate des fetchedResultsController habe ich den Table View Controller persönlich und mache das dann so wie im Pseudocode gepostet. Also ist deine Aussage wie auch mein Pseudocode richtig in dieser Situation. :)
    Widgetschmie.de • Life is too short for gadgets
  • Wird mit Deinen Pseudocode

    Quellcode

    1. - (void) tableView:commitEditingStyle:forRowAtIndexPath:
    2. {
    3. if (UITableViewCellEditingStyleDelete) {
    4. id delObj = [self objectAtIndexPath:<indexPath>];
    5. [controller deleteObject:delObj]; // Hier wird über den NSFetchedResultsController doch controller:didChangeObject:atIndexPath:newIndexPath: getriggert, oder?
    6. // den context evtl. speichern
    7. [tableView deleteRowsAtIndexPaths:withRowAnimation:]; // Hier wird die Zeile gelöscht
    8. }
    9. }

    und der Implementierung in controller:didChangeObject:atIndexPath:newIndexPath: also:

    Quellcode

    1. (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
    2. atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
    3. newIndexPath:(NSIndexPath *)newIndexPath
    4. {
    5. switch(type)
    6. {
    7. ...
    8. case NSFetchedResultsChangeDelete:
    9. [listenTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
    10. withRowAnimation:UITableViewRowAnimationFade]; // und hier wird die Zeile noch einmal gelöscht, oder?
    11. break;
    12. ...
    13. }
    14. }
    Alles anzeigen
    die Zeile dann nicht doppelt aus dem TableView gelöscht?
  • Mist, du hast Recht, ich hatte das übersehen:

    Quellcode

    1. if (!self.editing) {


    Sprich das deleteRowsAtIndexPaths (resp deleteSections) führe ich nur aus, wenn die table-view NICHT im edit modus ist. Und ich setze das delegate erst, wenn ich in den Edit-Modus wechsle, deshalb musste ich das so lösen, um swipe-to-delete funktional zu machen. Frag mich nicht, weshalb ich den delegate erst im setEditing setze... werde ich wohl mal umbauen und dadurch vereinfachen.
    Widgetschmie.de • Life is too short for gadgets