UICollectionView Assertion mislukt

Ik krijg deze fout bij het uitvoeren van insertItemsAtIndexPathsin 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 reloadDatabij 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:

  1. Ik heb UICollectionViewController gesubclasseerd en op initWithCollectionViewLayout:initialiseerde ik mijn NSFetchedResultsController.
  2. Laat een gedeelde klasse resultaten ophalen van een NSURLConnection en ontleden de JSON-tekenreeks (andere thread)
  3. Loop door de feed en maak mijn NSManagedObjects, voeg ze toe aan mijn NSManagedObjectContext, die de NSManagedObjectContextvan de hoofdthread heeft als een parentContext.
  4. Bewaar mijn context.
  5. Laat mijn NSFetchedResultsControllerde wijzigingen oppikken en in de wachtrij plaatsen.
  6. Op - (void)controllerDidChangeContent:zou ik de wijzigingen verwerken en toepassen op mijn UICollectionView.

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 performFetchverplaatst 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 UICollectionViewFlowLayoutof 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:

  1. Maak - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)sectionretourneer de werkelijke cel `count + 1 voor die sectie waar de kopweergave is.
  2. In - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPathreturn

    if ((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 viewDidLoadof waar u uw UICollectionView ook initialiseert:

    [self.collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:@"EmptyCell"];
    
  3. Gebruik moveItemAtIndexPath:of insertItemsAtIndexPaths: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 NSIndexPathaanmaakte. 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 0secties. 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:

https://stackoverflow.com/a/18411860/917104

Other episodes