59 votes

Échec de l'assertion UICollectionView

Je reçois cette erreur en effectuant insertItemsAtIndexPaths en UICollectionView

Échec d'assertion dans:

 -[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'
 

J'ai vérifié et ma source de données ne contient qu'un seul élément. Des idées sur pourquoi cela pourrait arriver? Si plus d'informations sont nécessaires, je peux certainement les fournir.

68voto

Jay Slupesky Points 1209

J'ai rencontré ce même problème lors de l'insertion de la première cellule en vue d'une collection. J'ai résolu le problème en modifiant mon code de façon à ce que j'appelle de la UICollectionView

- (void)reloadData

méthode lors de l'insertion de la première cellule, mais

- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths

lors de l'insertion de toutes les autres cellules.

Fait intéressant, j'ai aussi eu un problème avec

- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths

lors de la suppression de la dernière cellule. J'ai fait la même chose qu'avant: appelez - reloadData lors de la suppression de la dernière cellule.

11voto

neoneye Points 11545

L'insertion de la section n ° 0 juste avant l'insertion des cellules semble rendre UICollectionView heureux.

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

5voto

iWasRobbed Points 26926

J'ai posté une solution à ce problème ici: https://gist.github.com/iwasrobbed/5528897

Dans la catégorie privée en haut de votre fichier .m :

 @interface MyViewController ()
{
    BOOL shouldReloadCollectionView;
    NSBlockOperation *blockOperation;
}
@end
 

Ensuite, vos rappels de délégué seraient:

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

Le mérite de cette approche revient à Blake Watters.

4voto

Victor Bogdan Points 561

Ici, c'est un non-hack, docs de réponse au problème. Dans mon cas, il y avait une condition selon laquelle je serais de retour valide ou un néant complémentaire en vue d' collectionView:viewForSupplementaryElementOfKind:atIndexPath:. Après avoir rencontré le crash, j'ai vérifié les docs et voici ce qu'ils disent:

Cette méthode doit renvoyer toujours valide d'un objet de vue. Si vous ne voulez pas une complémentaire de vue, dans un cas particulier, votre objet de mise en page doit ne pas créer les attributs de ce point de vue. Alternativement, vous pouvez masquer vues en définissant la propriété hidden des attributs correspondants OUI ou régler la transparence de la propriété des attributs à 0. Pour masquer l'en-tête et pied de page vues dans un flux de mise en page, vous pouvez également définir la largeur et la hauteur de ces points de vue à 0.

Il y a d'autres façons de le faire, mais le moyen le plus rapide semble être:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section {
    return <condition> ? collectionViewLayout.headerReferenceSize : CGSizeZero;
}

3voto

Alex L Points 3954

Mon affichage de collection récupérait des éléments de deux sources de données et leur mise à jour était à l'origine de ce problème. Ma solution de contournement consistait à mettre la mise à jour des données et le rechargement de la vue de collecte en file d'attente:

 [[NSOperationQueue mainQueue] addOperationWithBlock:^{

                //Update Data Array
                weakSelf.dataProfile = [results lastObject]; 

                //Reload CollectionView
                [weakSelf.collectionView reloadItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
 }];
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X