2009-08-25 5 views
10

Die Dokumentation für NSFetchedResultsControllerDelegate geben Sie die folgenden BeispielcodeIst NSFetchedResultsControllerDelegate 'ChangeUpdate' Verhalten gebrochen?

- (void)controller:(NSFetchedResultsController *)controller 
    didChangeObject:(id)anObject 
     atIndexPath:(NSIndexPath *)indexPath 
    forChangeType:(NSFetchedResultsChangeType)type 
     newIndexPath:(NSIndexPath *)newIndexPath { 

    UITableView *tableView = self.tableView; 

    switch(type) { 

     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

    } 

} 

Wenn ich einen neuen NSManagedObject erstellen, NSFetchedResultsChangeInsert Feuer (super!). Wenn ich den Wert eines Attributs (für den Titel der Zelle verwendet) ändere, wird die NSFetchedResultsChangeUpdate ausgelöst. Leider wird der neue Titel nicht automatisch angezeigt, wenn ich die Tabelle, den Abschnitt oder die Zeile nicht neu lade. In der Tat, wenn der neue Name bewirkt, dass die Ergebnismenge anders sortiert wird, dann wird NSFetchedResultsChangeMove ausgelöst, und alles ist gut, da der bereitgestellte Code den gesamten Abschnitt neu lädt.

UITableView hat eine Methode reloadRowsAtIndexPaths: withRowAnimation so habe ich versucht, dies unter dem Block NSFetchedResultsChangeUpdate Code. Es ist in der Tat arbeiten ... aber die Dokumentation für diese spezielle Methode lesen, als ob ich es nicht brauchen (die letzte Zeile bemerken):

eine Reihe Nachladen bewirkt, dass die Tabellenansicht der Datenquelle verlangen eine neue Zelle für diese Zeile. Die Tabelle animiert diese neue Zelle, während sie die alte Zeile animiert. Rufen Sie diese Methode auf, wenn Sie den Benutzer warnen möchten, dass sich der Wert einer Zelle ändert. Wenn jedoch der Benutzer nicht benachrichtigt wird, ist wichtig-das heißt, Sie möchten nur den Wert ändern, den eine Zelle anzeigt, können Sie die Zelle für eine bestimmte Zeile abrufen und seinen neuen Wert festlegen.

Und ja, wenn ich log, was geschieht, wenn

[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 

auf einem NSFetchedResultsChangeUpdate aufgerufen wird, ist es in der Lage, die neueste ‚Name‘ Wert abrufen und in der textlabel Zelle gesetzt . Der Name wird nur in der Zelle gerendert, wenn ich ihn nicht neu lade. Auch wenn ich einfach auf die Zelle klicke, erscheint der Name. Beachten Sie, dass Sie ein neues verwaltetes Objekt erstellen müssen, um dieses Verhalten neu zu erstellen, und geben Sie ihm dann einen Name, der es veranlasst, FIRST in dem NSFetchedResultsController zu sortieren. Auf diese Weise wird NSFetchedResultsChangeMove nicht ausgelöst (was funktioniert, da der Abschnitt neu geladen wird).

Fehle ich etwas oder ist dieses erwartete Verhalten? Die 'Diskussion' für reloadRowsAtIndexPaths führt mich zu der Annahme, dass ich in der Lage sein sollte, einfach das textLabel der Zelle zu setzen, ohne die Zeile, den Abschnitt oder die Tabelle neu zu laden.

+0

Dies sollte wo sein rking. Ich mache das Gleiche. Für Änderungen an Attributen, wenn save: aufgerufen wird, gehen die MOC-Benachrichtigungen aus. NSFetchedResultsController sieht die Änderung und ruft meine configureCell: atIndexPath-Methode auf. Da greife ich die Werte und fülle die Zelle, und die Zelle wird sofort aktualisiert. Veröffentlichen Sie Ihren configureCell-Code. –

Antwort

3

Sie sollten [cell setNeedsLayout] oder/und [cell setNeedsDisplay] aufrufen, damit die Zelle abhängig von der Implementierung Ihrer Zelle aktualisiert wird.

Wenn Sie die Zelle von Subviews wie üblich zusammenstellen, verlassen Sie sich auf - layoutSubviews, Sie sollten also [cell setNeedsLayout] anrufen. Wenn Sie die Zelle direkt mit – drawRect: zeichnen, sollten Sie [cell setNeedsDisplay] anrufen.

Wenn Sie sowohl Komposition als auch Zeichnung verwenden, sollten Sie beide aufrufen.

1

Obwohl es wahr ist, dass Sie die Zelle nicht neu laden müssen, um die Änderungen stattfinden zu lassen, müssen Sie daran denken, dass das iPhone die Zeichnung so weit wie möglich zwischenspeichert. Sobald Sie Ihre Zelle neu konfiguriert haben, müssen Sie setNeedsDisplay auf der Zelle aufrufen, um das Neuzeichnen auszulösen.

+0

An diesem Punkt - was ist der beste Weg, um die Zelle, die auf dem Bildschirm ist? tableview: cellForRowAtIndexPath: erstellt eine neue Zelle, während ich einen Verweis auf die Zelle auf dem Bildschirm bekommen möchte ... oder? –

+0

Normalerweise müssen Sie für jede Zelle, die Sie konfigurieren, setNeedsDisplay aufrufen. Wenn es sich um eine neue Zelle handelt, hat es keinen Cache, also keine Sorge, wenn Sie neu konfigurieren, wird es neu gezeichnet. Wenn Sie wirklich wissen wollen, welche Bildschirme sichtbar sind, werfen Sie einen Blick auf meine Antwort auf: http://stackoverflow.com/questions/996515/getting-visible-cell-from-guidableview-pagingenabled/1566432#1566432 –

1

Obwohl es nicht ausdrücklich erwähnt, haben Sie wirklich sicher zu machen habe, dass Sie die Delegatmethoden für controllerWillChangeContent sind: und controllerDidChangeContent:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { 
    [self.tableView beginUpdates]; 
} 

und

- (void)controllerDidChangeContent:(BSFetchedResultsController *)controller { 
    @try { 
     [self.tableView endUpdates]; 
    } 
    @catch (NSException * e) { 
     NSLog(@"caught exception: %@: %@", [e name], [e description]); 
    } 
    @finally { } 
} 

Die NSFRC kann Feuer Aus mehreren Controllern: didChangeObject: atIndexPath: forChangeType: newIndexPath: Methoden während einer einzelnen Änderung. Erwarten Sie daher nicht, dass die Tabellenzeile unmittelbar nach jedem einzelnen aktualisiert wird. Sie werden nur aktualisiert, nachdem Sie endUpdates auf dem Tisch aufgerufen haben.

0

die Benutzeroberfläche aufrufen Logik Aktualisierung in Hauptthread mein Problem gelöst (beide [cell setNeedsLayout] & [cell setNeedsDisplay] nicht funktioniert für mich):

... 
case NSFetchedResultsChangeUpdate: 
    dispatch_async(dispatch_get_main_queue(), { 
     [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
    }); 
    break; 
... 

Was mehr ist (nicht zu diesem Problem wird aber nützlich sein,), sollten Sie besser die newIndexPath verwenden, wenn sie verfügbar ist:

... 
case NSFetchedResultsChangeUpdate: 
    dispatch_async(dispatch_get_main_queue(), { 
     NSIndexPath * targetIndexPath = (newIndexPath ?: indexPath) 
     [self configureCell:[tableView cellForRowAtIndexPath:targetIndexPath] 
       atIndexPath:targetIndexPath]; 
    }); 
    break; 
... 
+1

diejenigen Ereignisse sind bereits auf dem Hauptthread, was Sie tun, ist Shunting in die nächste Ereignisschleife, obwohl GCD ist nicht 100% gut, Leistung nach Verzögerung ist zuverlässiger. – malhal

+0

@malhaler guter Punkt. – Kjuly