2013-01-16 2 views
6

Ich habe eine einfache Tabellenansicht mit 1 Abschnitt und 2 Zeilen. Ich verwende eine NSFetchedResultsController, um die Tabelle mit CoreData synchronisiert zu halten. Ich ändere eine der Zeilen auf der CD, die eine Tabellenansichtszelle zum Aktualisieren und Verschieben auslöst. Das Problem ist, dass wenn cellForRowAtIndexPath: während der NSFetchedResultsChangeUpdate aufgerufen wird, die falsche Zelle zurückgegeben wird (das macht Sinn b/c die Zellen wurden noch nicht verschoben). Daher wird die falsche Zelle mit den neu aktualisierten Daten aktualisiert. Danach wird die NSFetchedResultsChangeMove Nachricht behandelt, so dass die Zellen ihre Plätze tauschen (der Inhalt beider Zellen wird nicht aktualisiert, da es sich nur um einen Anruf handelt). Das Ergebnis ist, dass beide Zellen die Daten von der neu aktualisierten CD-Entität wiedergeben. Das erneute Laden der Tabelle behebt das Problem. Ich betreibe iOS 6. Mit anderen Worten, wenn die Zelle bei Index 0 Entität A darstellt und Index 1 Entität B darstellt und I Entität A zu A 'aktualisieren, so dass die 2 Zellen umgekehrte Reihenfolge, das Ergebnis ist, dass I siehe 0: A '1: A wenn ich 0: B, 1: A' erwarten würde.Gleichzeitige Verschiebung und Aktualisierung von UITableViewCell und NSFetchedResultsController

- (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: 
//the wrong cell is updated here 
      [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; 
//this code produces errors too 
      //[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      //[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

Antwort

4

Ich würde Ihnen empfehlen, in diesem einen Blick zu nehmen blogpost

den Bug Fixing ist einfach. Verlassen Sie sich ganz auf das Verhalten von UITableView ich oben beschrieben und ersetzen Sie den Aufruf von configureCell: atIndexPath: mit den reloadRowsAtIndexPaths: withRowAnimation: Methode, die automatisch das Richtige tun wird:

case NSFetchedResultsChangeUpdate: 
    [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 
    break; 
+0

Unabhängig davon erhalte ich keine Update-Ereignisse, wenn ich moveRowAtIndexPath anstelle von delete + insert combo verwende. Irgendeine Idee, was kann die Ursache sein? – Andy

6

Eine Lösung ist:

[self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:newIndexPath ? newIndexPath : indexPath]; 

den neuen Indexpfad verwenden, wenn seine während des Updates versorgt. Und dann benutze löschen und einfügen statt verschieben. Ich würde immer noch gerne wissen, ob irgendjemand anderes etwas zu sagen hat.

+0

Süß! Verbrachte 3 Tage auf diesem ... Ein Dokument zum Thema: http://developer.apple.com/library/ios/#releasenotes/iPhone/NSFetchedResultsChangeMoveReportedAsNSFetchedResultsChangeUpdate/ –

+0

Ah interessant. Es ist gut zu wissen, dass ich nichts falsch gemacht habe :) Danke! – Tylerc230

1

Die einzige Lösung, die für arbeitete me: Thomas Worrall's Blog: Reordering rows in a UITableView with Core Data

Zunächst einmal Sein notwendig, ein Attribut in Ihrem NSManagedObject erstellen die letzte Bestellung des Objekts zu hängen.

@interface MyEntity : NSManagedObject 

@property (nonatomic, retain) NSNumber * lastOrder; 

@end 

Dann eine Eigenschaft in Ihrem ViewController.m erklärt:

@interface ViewController() 

@property (nonatomic) BOOL isReordering; 

@end 

In Verfahren Tableview: moveRowAtIndexPath: toIndexPath:, die Änderungen in einem temporären Array verwalten:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 
{ 
    _isReordering = YES; 

    NSMutableArray *arrayNewOrder = [[_fetchedResultsController fetchedObjects] mutableCopy]; 
    MyEntity *myManagedObject = [arrayNewOrder objectAtIndex:fromIndexPath.row]; 
    [arrayNewOrder removeObjectAtIndex:fromIndexPath.row]; 
    [arrayNewOrder insertObject:myManagedObject atIndex:toIndexPath.row]; 

    for (int i=0; i < [arrayNewOrder count]; i++) 
    { 
     myManagedObject = [arrayNewOrder objectAtIndex:i]; 
     myManagedObject.lastOrder = [NSNumber numberWithInt:i]; 
    } 

    _isReordering = NO; 
    NSError *error; 
    BOOL success = [self.fetchController performFetch:&error]; 
    if (!success) 
    { 
     // Handle error 
    } 

    success = [[self managedObjectContext] save:&error]; 
    if (!success) 
    { 
     // Handle error 
    } 
} 

Thomas erklärt über führen holen und speichern Kontext:

Ich bin mir nicht ganz sicher, warum das Fetch zuerst durchgeführt werden muss, aber es hat eine Tonne verrückter Bugs behoben, als ich es getan habe!

Der letzte Trick NSFetchedResultsController Delegatmethode Controller ist der Umgang: didChangeObject: atIndexPath: forChangeType: newIndexPath:, speziell in NSFetchedResultsChangeMove:

case NSFetchedResultsChangeMove: 
{ 
    if (!_isReordering) 
    { 
     [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
         withRowAnimation:UITableViewRowAnimationFade]; 
     [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
         withRowAnimation:UITableViewRowAnimationFade]; 
    } 

    break; 
} 

Es funktionierte für mich! Ich hoffe es hilft dir!

+0

Ist es ein Fehler in FRC, dass wir moveRow nicht in der Tabellenansicht verwenden können und gezwungen sind, + Einfügen zu löschen? – Andy

3

Wenn Sie configureCell aufrufen, suchen Sie den IndexPath basierend auf dem übergebenen Objekt. Sowohl indexPath als auch newIndexPath sind zu diesem Zeitpunkt unzuverlässig. Zum Beispiel:

case NSFetchedResultsChangeUpdate: 
    myPath = [controller indexPathForObject:anObject]; 
    if (myPath) { 
     [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:myPath]; 
    } 

    break; 
Verwandte Themen