Sto indagando un bug schiantarsi con un UICollectionView
tramite Crashlytics che richiede generalmente questa forma:È possibile utilizzare sia reloadItemsAtIndexPaths che reloadData se il conteggio dei dati può variare?
irreversibile: NSInternalInconsistencyException valido aggiornamento: numero non valido di voci nella sezione 0. Il numero di elementi contenuti in uno esistente la sezione successiva all'aggiornamento (25) deve essere uguale al numero di elementi contenuti in tale sezione prima dell'aggiornamento (27), più o meno il numero di elementi inseriti o cancellati da quella sezione (1 inserito, 1 cancellato) e più o meno il numero di elementi spostati in o fuori da quella sezione (0 spostato in, 0 spostato fuori).
Credo che questo sia causato perché ho un CollectionView che si aggiorna periodicamente con i dati da un server, e che i dati dal server può avere più o meno elementi che sono contenuti nel client UICollectionViewDataSource
.
Quando ricevo nuovi dati dal server, chiamo reloadData
sulla mia vista raccolta.
Tuttavia, è possibile che, a causa dell'interazione dell'utente con la mia vista raccolta prima che il download della rete fosse completato, avevo chiamato reloadItemsAtIndexPaths
poco prima. reloadItemsAtIndexPaths
non sembra terminare per almeno alcune centinaia di ms e molti cicli del processore. Quindi, questo arresto anomalo, quando il dataSource viene aggiornato nel mezzo di un reloadItemsAtIndexPaths
.
Esiste una forma "immediata" di reloadItemsAtIndexPaths
? O devo sempre chiamare reloadData
dato il mio caso d'uso, che sembra aggiornare immediatamente tutto e lasciare il UICollectionView
in buono stato alla fine.
Modifica
Ecco quello che hanno fatto, per consigliare dai TwoStraws:
// Prevent data source from batch updating while we work
self.dataSource.locked = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:@[indexPath]];
} completion:^(BOOL finished) {
self.dataSource.locked = NO;
}];
Poi nella mia classe di origine di dati, dopo aver ricevuto i risultati dal server, ho sempre chiama assignResults
:
- (void)assignResults:(NSMutableArray *)newResults {
if (!self.locked) {
self.results = newResults;
[self.delegate handleDataSourceUpdated:self];
} else {
self.pendingResults = newResults;
}
}
- (void)setLocked:(BOOL)locked {
_locked = locked;
if (!locked && self.pendingResults) {
[self assignResults:self.pendingResults];
self.pendingResults = nil;
}
}
Come utente puoi vedere, i risultati vengono assegnati solo se l'origine dati non è bloccata; altrimenti vengono assegnati quando la sorgente dati è sbloccata dallo UICollectionViewController
. Nota che tutti questi metodi stanno accadendo sul thread principale, quindi non devo preoccuparmi della sincronizzazione della mia proprietà booleana, locked
.
Non è chiaro per me che UICollectionView continuerà a "puntare" sull'origine dati A durante l'intera durata di un reloadItemsAtIndexPaths in questo approccio. Sappiamo se ciò che UICollectionView fa/dovrebbe funzionare? – esilver
È possibile rilevare quando una vista raccolta inizia e termina gli aggiornamenti utilizzando il blocco di completamento sul metodo 'performBatchUpdates()' - AFAIK che deve essere attivato solo quando il caricamento è definitivamente terminato. Se si dispone di una situazione molto complicata, potrebbe essere necessario utilizzare una proprietà booleana per contrassegnare "Sto per avviare gli aggiornamenti" e "Ho sicuramente finito, è sicuro da copiare". – TwoStraws
Grazie @twostraws: questo è quello che ho fatto come puoi vedere nelle mie modifiche sopra. – esilver