Ik krijg deze fout bij het uitvoeren van insertItemsAtIndexPaths
in UICollectionView
Bewering mislukt in:
-[UICollectionViewData indexPathForItemAtGlobalIndex:],
/SourceCache/UIKit/UIKit-2372/UICollectionViewData.m:442
2012-09-26 18:12:34.432
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'request for index path for global index 805306367
when there are only 1 items in the collection view'
Ik heb het gecontroleerd en mijn gegevensbron bevat slechts één element.
Enig idee waarom dit zou kunnen gebeuren? Als er meer informatie nodig is, kan ik dat zeker geven.
Antwoord 1, autoriteit 100%
Ik kwam hetzelfde probleem tegen toen ik de eerste cel in een verzamelingsweergave invoegde. Ik heb het probleem opgelost door mijn code te wijzigen zodat ik de UICollectionView roep
- (void)reloadData
methode bij het invoegen van de eerste cel, maar
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
bij het invoegen van alle andere cellen.
Interessant genoeg had ik ook een probleemmet
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
bij het verwijderen van de laatste cel. Ik deed hetzelfde als voorheen: bel gewoon reloadData
bij het verwijderen van de laatste cel.
Antwoord 2, autoriteit 16%
Het lijkt UICollectionView blij te maken met het invoegen van sectie#0 net voor het invoegen van cellen.
NSArray *indexPaths = /* indexPaths of the cells to be inserted */
NSUInteger countBeforeInsert = _cells.count;
dispatch_block_t updates = ^{
if (countBeforeInsert < 1) {
[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:0]];
}
[self.collectionView insertItemsAtIndexPaths:indexPaths];
};
[self.collectionView performBatchUpdates:updates completion:nil];
Antwoord 3, autoriteit 7%
Ik heb hier een oplossing voor dit probleem geplaatst: https://gist.github.com/iwasrobbed/5528897
In de privécategorie bovenaan uw .m
-bestand:
@interface MyViewController ()
{
BOOL shouldReloadCollectionView;
NSBlockOperation *blockOperation;
}
@end
Dan zijn de callbacks van uw gedelegeerde:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
shouldReloadCollectionView = NO;
blockOperation = [NSBlockOperation new];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
__weak UICollectionView *collectionView = self.collectionView;
switch (type) {
case NSFetchedResultsChangeInsert: {
[blockOperation addExecutionBlock:^{
[collectionView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
}];
break;
}
case NSFetchedResultsChangeDelete: {
[blockOperation addExecutionBlock:^{
[collectionView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
}];
break;
}
case NSFetchedResultsChangeUpdate: {
[blockOperation addExecutionBlock:^{
[collectionView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex]];
}];
break;
}
default:
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
__weak UICollectionView *collectionView = self.collectionView;
switch (type) {
case NSFetchedResultsChangeInsert: {
if ([self.collectionView numberOfSections] > 0) {
if ([self.collectionView numberOfItemsInSection:indexPath.section] == 0) {
shouldReloadCollectionView = YES;
} else {
[blockOperation addExecutionBlock:^{
[collectionView insertItemsAtIndexPaths:@[newIndexPath]];
}];
}
} else {
shouldReloadCollectionView = YES;
}
break;
}
case NSFetchedResultsChangeDelete: {
if ([self.collectionView numberOfItemsInSection:indexPath.section] == 1) {
shouldReloadCollectionView = YES;
} else {
[blockOperation addExecutionBlock:^{
[collectionView deleteItemsAtIndexPaths:@[indexPath]];
}];
}
break;
}
case NSFetchedResultsChangeUpdate: {
[blockOperation addExecutionBlock:^{
[collectionView reloadItemsAtIndexPaths:@[indexPath]];
}];
break;
}
case NSFetchedResultsChangeMove: {
[blockOperation addExecutionBlock:^{
[collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
}];
break;
}
default:
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// Checks if we should reload the collection view to fix a bug @ http://openradar.appspot.com/12954582
if (shouldReloadCollectionView) {
[self.collectionView reloadData];
} else {
[self.collectionView performBatchUpdates:^{
[blockOperation start];
} completion:nil];
}
}
Het krediet voor deze aanpak gaat naar Blake Watters.
Antwoord 4, autoriteit 6%
Hier is een niet-hack, op documenten gebaseerd antwoord op het probleem. In mijn geval was er een voorwaarde volgens welke ik een geldige of een nul aanvullende weergave van collectionView:viewForSupplementaryElementOfKind:atIndexPath:
zou retourneren. Nadat ik de crash tegenkwam, heb ik de documenten gecontroleerd en dit is wat ze zeggen:
Deze methode moet altijd een geldig view-object retourneren. Als je niet wilt
een aanvullende weergave in een bepaald geval, moet uw lay-outobject
niet de kenmerken voor die weergave maken. U kunt ook verbergen
weergaven door de verborgen eigenschap van de bijbehorende attributen in te stellen
op YES of stel de alpha-eigenschap van de attributen in op 0. Verbergen
kop- en voettekstweergaven in een stroomlay-out, u kunt ook de breedte instellen
en hoogte van die weergaven tot 0.
Er zijn andere manieren om dit te doen, maar de snelste lijkt te zijn:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero;
}
Antwoord 5, autoriteit 4%
Mijn collectieweergave was het ophalen van items uit twee gegevensbronnen en het bijwerken hiervan veroorzaakte dit probleem. Mijn tijdelijke oplossing was om de gegevensupdate en het opnieuw laden van de collectie samen in de wachtrij te plaatsen:
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//Update Data Array
weakSelf.dataProfile = [results lastObject];
//Reload CollectionView
[weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
}];
Antwoord 6
Controleer of u het juiste aantal elementen retourneert in de UICollectionViewDataSource
-methoden:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
en
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
Antwoord 7
Ik kwam dit probleem ook tegen. Dit is wat er met mij is gebeurd:
- Ik heb UICollectionViewController gesubclasseerd en op
initWithCollectionViewLayout:
initialiseerde ik mijnNSFetchedResultsController
. - Laat een gedeelde klasse resultaten ophalen van een NSURLConnection en ontleden de JSON-tekenreeks (andere thread)
- Loop door de feed en maak mijn
NSManagedObjects
, voeg ze toe aan mijnNSManagedObjectContext
, die deNSManagedObjectContext
van de hoofdthread heeft als eenparentContext
. - Bewaar mijn context.
- Laat mijn
NSFetchedResultsController
de wijzigingen oppikken en in de wachtrij plaatsen. - Op
- (void)controllerDidChangeContent:
zou ik de wijzigingen verwerken en toepassen op mijnUICollectionView
.
Af en toe kreeg ik de foutmelding die de OP krijgt en ik kon niet achterhalen waarom.
Om dit probleem op te lossen, heb ik de NSFetchedResultsController
-initialisatie en performFetch
verplaatst naar mijn - viewDidLoad
-methode en dit probleem is nu verdwenen. Het is niet nodig om [collectionView reloadData]
of iets anders aan te roepen en alle animaties werken naar behoren.
Hopelijk helpt dit!
Antwoord 8
Het lijkt erop dat het probleem optreedt wanneer u een cel invoegt of verplaatst naar een sectie die een aanvullende kop- of voettekstweergave bevat (met UICollectionViewFlowLayout
of een daarvan afgeleide lay-out) en de sectie een telling van 0 cellen voor het invoegen / verplaatsen.
Ik kon de crash alleen omzeilen en toch de animaties behouden door een lege en onzichtbare cel te hebben in de sectie met de aanvullende koptekstweergave, zoals deze:
- Maak
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section
retourneer de werkelijke cel `count + 1 voor die sectie waar de kopweergave is. -
In
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
returnif ((indexPath.section == YOUR_SECTION_WITH_THE_HEADER_VIEW) && (indexPath.item == [self collectionView:collectionView numberOfItemsInSection:indexPath.section] - 1)) { cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"EmptyCell" forIndexPath:indexPath]; }
… een lege cel voor die positie. Vergeet niet om het hergebruik van cellen te registreren in
viewDidLoad
of waar u uw UICollectionView ook initialiseert:[self.collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:@"EmptyCell"];
- Gebruik
moveItemAtIndexPath:
ofinsertItemsAtIndexPaths:
zonder te crashen.
Antwoord 9
Hier is een oplossing voor deze bug die ik in mijn projecten heb gebruikt. Ik dacht dat ik die hier zou posten voor het geval iemand het waardevol zou vinden.
@interface FetchedResultsViewController ()
@property (nonatomic) NSMutableIndexSet *sectionsToAdd;
@property (nonatomic) NSMutableIndexSet *sectionsToDelete;
@property (nonatomic) NSMutableArray *indexPathsToAdd;
@property (nonatomic) NSMutableArray *indexPathsToDelete;
@property (nonatomic) NSMutableArray *indexPathsToUpdate;
@end
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self resetFetchedResultControllerChanges];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.sectionsToAdd addIndex:sectionIndex];
break;
case NSFetchedResultsChangeDelete:
[self.sectionsToDelete addIndex:sectionIndex];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[self.indexPathsToAdd addObject:newIndexPath];
break;
case NSFetchedResultsChangeDelete:
[self.indexPathsToDelete addObject:indexPath];
break;
case NSFetchedResultsChangeUpdate:
[self.indexPathsToUpdate addObject:indexPath];
break;
case NSFetchedResultsChangeMove:
[self.indexPathsToAdd addObject:newIndexPath];
[self.indexPathsToDelete addObject:indexPath];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
if (self.sectionsToAdd.count > 0 || self.sectionsToDelete.count > 0 || self.indexPathsToAdd.count > 0 || self.indexPathsToDelete > 0 || self.indexPathsToUpdate > 0)
{
if ([self shouldReloadCollectionViewFromChangedContent])
{
[self.collectionView reloadData];
[self resetFetchedResultControllerChanges];
}
else
{
[self.collectionView performBatchUpdates:^{
if (self.sectionsToAdd.count > 0)
{
[self.collectionView insertSections:self.sectionsToAdd];
}
if (self.sectionsToDelete.count > 0)
{
[self.collectionView deleteSections:self.sectionsToDelete];
}
if (self.indexPathsToAdd.count > 0)
{
[self.collectionView insertItemsAtIndexPaths:self.indexPathsToAdd];
}
if (self.indexPathsToDelete.count > 0)
{
[self.collectionView deleteItemsAtIndexPaths:self.indexPathsToDelete];
}
for (NSIndexPath *indexPath in self.indexPathsToUpdate)
{
[self configureCell:[self.collectionView cellForItemAtIndexPath:indexPath]
atIndexPath:indexPath];
}
} completion:^(BOOL finished) {
[self resetFetchedResultControllerChanges];
}];
}
}
}
// This is to prevent a bug in UICollectionView from occurring.
// The bug presents itself when inserting the first object or deleting the last object in a collection view.
// http://stackoverflow.com/questions/12611292/uicollectionview-assertion-failure
// This code should be removed once the bug has been fixed, it is tracked in OpenRadar
// http://openradar.appspot.com/12954582
- (BOOL)shouldReloadCollectionViewFromChangedContent
{
NSInteger totalNumberOfIndexPaths = 0;
for (NSInteger i = 0; i < self.collectionView.numberOfSections; i++)
{
totalNumberOfIndexPaths += [self.collectionView numberOfItemsInSection:i];
}
NSInteger numberOfItemsAfterUpdates = totalNumberOfIndexPaths;
numberOfItemsAfterUpdates += self.indexPathsToAdd.count;
numberOfItemsAfterUpdates -= self.indexPathsToDelete.count;
BOOL shouldReload = NO;
if (numberOfItemsAfterUpdates == 0 && totalNumberOfIndexPaths == 1)
{
shouldReload = YES;
}
if (numberOfItemsAfterUpdates == 1 && totalNumberOfIndexPaths == 0)
{
shouldReload = YES;
}
return shouldReload;
}
- (void)resetFetchedResultControllerChanges
{
[self.sectionsToAdd removeAllIndexes];
[self.sectionsToDelete removeAllIndexes];
[self.indexPathsToAdd removeAllObjects];
[self.indexPathsToDelete removeAllObjects];
[self.indexPathsToUpdate removeAllObjects];
}
Antwoord 10
In mijn geval was het probleem de manier waarop ik mijn NSIndexPath
aanmaakte. Om bijvoorbeeld de 3e cel te verwijderen, in plaats van :
NSIndexPath* indexPath = [NSIndexPath indexPathWithIndex:2];
[_collectionView deleteItemsAtIndexPaths:@[indexPath]];
Ik moest doen:
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:2 inSection:0];
[_collectionView deleteItemsAtIndexPaths:@[indexPath]];
Antwoord 11
Controleer of u de juiste waarde retourneert in numberOfSectionsInCollectionView:
De waarde die ik gebruikte om secties te berekenen was nul, dus 0
secties. Dit veroorzaakte de uitzondering.
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
NSInteger sectionCount = self.objectThatShouldHaveAValueButIsActuallyNil.sectionCount;
// section count is wrong!
return sectionCount;
}
Antwoord 12
Voor alle duidelijkheid, ik kwam hetzelfde probleem tegen en voor mij was de oplossing om de header te verwijderen (uitschakelen in de .xib) en omdat ze niet meer nodig waren, deze methode verwijderd. Daarna lijkt alles in orde.
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementary
ElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
Antwoord 13
Ik heb dit probleem zelf ondervonden. Alle antwoorden hier leken problemen op te leveren, behalve die van Alex L. Verwijzen naar de update leek het antwoord te zijn. Hier is mijn definitieve oplossing:
- (void)addItem:(id)item {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if (!_data) {
_data = [NSMutableArray arrayWithObject:item];
} else {
[_data addObject:item];
}
[_collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:_data.count-1 inSection:0]]];
}];
}
Antwoord 14
De tijdelijke oplossing die echt werkt, is om een hoogte van 0 te retourneren als de cel op het indexpad van uw aanvullende weergave er niet is (eerste keer laden, u hebt de rij verwijderd, enz.). Zie hier mijn antwoord: