2009-08-25 5 views
10

La documentazione per NSFetchedResultsControllerDelegate fornire il seguente codice di esempioIl comportamento di NSFetchedResultsControllerDelegate 'ChangeUpdate' è rotto?

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

    } 

} 

Quando creo un nuovo NSManagedObject, NSFetchedResultsChangeInsert incendi (grandi!). Quando cambio il valore di un attributo (utilizzato per il titolo della cella), viene attivato lo NSFetchedResultsChangeUpdate. Sfortunatamente, il nuovo titolo non viene visualizzato automaticamente a meno che non ricarichi la tabella, la sezione o la riga. Infatti, se il nuovo nome fa sì che il set di risultati sia ordinato in modo diverso, allora gli spari NSFetchedResultsChangeMove si attivano e tutto procede bene poiché il codice fornito ricarica l'intera sezione.

UITableView ha un metodo reloadRowsAtIndexPaths: withRowAnimation quindi provato utilizzando questo sotto il blocco di codiceNSFetchedResultsChangeUpdate. E 'effettivamente funziona ... ma la documentazione di questo metodo specifico letto come se io non ne ho bisogno (notate l'ultima riga):

ricaricamento di fila fa sì che la vista tavolo per chiedere la sua fonte di dati per una nuova cella per quella riga. La tabella anima quella nuova cella in quanto anima la riga precedente. Chiamare questo metodo se si desidera avvisare l'utente che il valore di una cella sta cambiando. Se, tuttavia, la notifica all'utente non è importante, vale a dire che si desidera modificare il valore di una cella è visualizzazione-è possibile ottenere la cella per una riga particolare e impostare il suo nuovo valore.

E sì, se accedo ciò che sta accadendo, quando

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

viene invocata su un NSFetchedResultsChangeUpdate, è in grado di recuperare il valore più recente 'nome' e metterlo in textLabel della cellula . Il nome non viene semplicemente visualizzato nella cella a meno che non lo ricarichi. Anche se faccio semplicemente clic sulla cella, compare il nome. Si noti che per ricreare questo comportamento, è necessario creare un nuovo oggetto gestito e quindi dargli un nome che lo induca a ordinare PRIMO in NSFetchedResultsController. In questo modo, NSFetchedResultsChangeMove non viene attivato (il che funziona poiché ricarica la sezione).

Mi manca qualcosa o è questo comportamento previsto? La "discussione" per reloadRowsAtIndexPaths mi porta a credere che dovrei essere in grado di impostare semplicemente la textLabel della cella senza ricaricare riga, sezione o tabella.

+0

Questo dovrebbe essere wo rking. Sto facendo la stessa cosa Per le modifiche agli attributi, quando si salva: viene chiamato, le notifiche MOC si spengono. NSFetchedResultsController vede la modifica e chiama il mio configureCell: metodo atIndexPath. Lì afferro i valori e riempio la cella, e la cella viene aggiornata immediatamente. Pubblica il tuo codice configureCell. –

risposta

3

È necessario chiamare [cell setNeedsLayout] o/e [cell setNeedsDisplay] affinché la cella venga aggiornata, a seconda dell'implementazione della cella.

Se si compila la cella delle sottoview come facciamo abitualmente, ci si basa su - layoutSubviews, quindi è necessario chiamare [cell setNeedsLayout].

Se si disegna la cella direttamente con – drawRect:, è necessario chiamare [cell setNeedsDisplay].

Se si utilizza sia la composizione che il disegno, è necessario chiamare entrambi.

1

Mentre è vero che non è necessario ricaricare la cella per fare in modo che le modifiche abbiano luogo, è necessario ricordare che l'iPhone memorizza nella cache il disegno il più possibile. Una volta che hai configurato la tua cella, devi chiamare setNeedsDisplay sulla cella per attivare il ridisegno.

+0

A questo punto, qual è il modo migliore per ottenere la cella visualizzata sullo schermo? tableview: cellForRowAtIndexPath: crea una nuova cella mentre voglio ottenere un riferimento alla cella sullo schermo ... giusto? –

+0

Normalmente, non è necessario, per ogni cella che si sta configurando, chiamare setNeedsDisplay. Se è una nuova cella, non ha una cache, quindi non preoccuparti, se stai riconfigurando, si ridisegnerà. Se vuoi veramente sapere quali schermate sono visibili, dai un'occhiata alla mia risposta a: http://stackoverflow.com/questions/996515/getting-visible-cell-from-uitableview-pagingenabled/1566432#1566432 –

1

Anche se non è stato esplicitamente affermato, che hai effettivamente avuto modo di fare in modo di includere i metodi delegato per controllerWillChangeContent: e controllerDidChangeContent:

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

e

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

Il NSFRC può sparare off multiple controller: didChangeObject: atIndexPath: forChangeType: newIndexPath: metodi durante qualsiasi modifica, quindi non aspettatevi che la riga della tabella si aggiorni subito dopo ciascuna di esse. Verranno aggiornati solo dopo aver chiamato endUpdates sul tavolo.

0

Chiamando l'interfaccia utente di aggiornamento logica nel thread principale Ha risolto il problema (entrambi [cell setNeedsLayout] & [cell setNeedsDisplay] non funziona per me):

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

Cosa c'è di più (non su questo problema, ma sarà utile), è meglio scegliere di utilizzare il newIndexPath se disponibile:

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

quelli gli eventi sono già sul thread principale, quello che stai facendo è lo shunt al prossimo ciclo degli eventi, sebbene GCD non sia al 100% buono, esegui dopo che il ritardo è più affidabile. – malhal

+0

@malhal buon punto. – Kjuly