Ошибка утверждения в -[UICollectionViewData numberOfItemsBeforeSection:]

Используя IOS 6 и новый UICollectionView, у меня есть 2 UICollectionViews в UIViewController, и с помощью Storyboard я получаю следующую ошибку, когда нажимаю UICollectionView. Я не уверен, почему это происходит, и Google возвращает 0 результатов для этой ошибки...

2012-10-13 20:30:06.421 MyApp[768:907] Ошибка утверждения в -[UICollectionViewData numberOfItemsBeforeSection:], /SourceCache/UIKit/UIKit-2372/UICollectionViewData.m:415
2012-10-13 20: 30:06.427 MyApp[768:907] Завершение приложения из-за необработанного исключения «NSInternalInconsistencyException», причина: «запрос количества элементов перед разделом 2147483647, когда в представлении коллекции есть только 1 раздел»

Я вообще не использую разделы и установил только 1 раздел:

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}

Это происходит (кажется, случайным образом), когда я нажимаю на область UICollectionView, у меня есть фрагмент кода для удаления элемента из UICollectionView:

[tilesArray removeObjectAtIndex:indexPath.row];
[self.tilesCollectionView reloadData];        
[resultsArray addObject:str];
[self.resultsCollectionView reloadData];

Вот как массивы подключаются к UICollectionViews

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    if(collectionView.tag == 2)
        return [resultsArray count];
    else
        return [tilesArray count];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    if(collectionView.tag == 2)
    {
        static NSString *cellIdentifier = @"ResultCell";
        ResultCell *cell = (ResultCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

        cell.mainImage.image = [UIImage imageNamed:currentArtContainer.tileMainImage];

        NSString *cellData = [resultsArray objectAtIndex:indexPath.row];
        [cell.titleLabel setText:cellData];

        return cell;
    }
    else
    {
        static NSString *cellIdentifier = @"TileCell";
        TileCell *cell = (TileCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];

        cell.topImage.image = [UIImage imageNamed:currentArtContainer.tileTopImage];

        cell.mainImage.image = [UIImage imageNamed:currentArtContainer.tileMainImage];

        NSString *cellData = [tilesArray objectAtIndex:indexPath.row];
        [cell.titleLabel setText:cellData];

        return cell;
    }
}

Пожалуйста, помогите мне решить эту проблему. Даже не уверен, как я могу поймать исключение, поскольку оно исходит из внутреннего кода UICollectionView. Также забыл упомянуть, что я использую собственный макет для UICollectionView, но я не знаю, связано ли это с этой проблемой.

Еще раз спасибо за вашу помощь, ребята!

Изменить: я получаю исключение после вызова всех методов макета... Вот мой собственный макет... Я до сих пор не знаю, как его исправить, поскольку все мои блоки try catch не перехватывают ошибку. ..

- (void)prepareLayout
{
    NSLog(@"In prepareLayout.");

    [super prepareLayout];
    _cellCount = [[self collectionView] numberOfItemsInSection:0];
}

- (CGSize)collectionViewContentSize
{
    NSLog(@"In collectionViewContentSize.");
    @try {

       CGSize csize = [self collectionView].frame.size;
        NSLog(@"In here.");
        return csize;
    }
    @catch(NSException *exception)
    {
        NSLog(@"collectionViewContentSize %@: %@",[exception name],[exception reason]);
    }

}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    @try {

    NSLog(@"In layoutAttributesForItemAtIndexPath. item %d", indexPath.row);


    UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

    attributes.size = CGSizeMake(ITEM_SIZE, ITEM_SIZE);

    NSLog(@"firstRun %d", self.firstRun);
    //NSLog(@"panStatus %@", self.panStatus);

    if (self.firstRun == 0) {
        CGFloat x = 0, y = 0;
        CGFloat boundx = self.collectionView.frame.size.width * 0.70;   // Use the whole canvas
        CGFloat boundy = self.collectionView.frame.size.height * 0.70; // Use the whole canvas
        while (x < 50 || x > boundx) x = arc4random() % (int)boundx + indexPath.item;
        while (y < 50 || y > boundy) y = arc4random() % (int)boundy + indexPath.item;

        NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
        if (self.positions)
            dictionary = [self.positions mutableCopy];
        [dictionary setObject:[NSValue valueWithCGPoint:CGPointMake(x, y)] forKey:@(indexPath.item)];
        if (!self.positions)
            self.positions = [[NSDictionary alloc] init];
        self.positions = dictionary;

        attributes.center = CGPointMake(x, y);
    } else if (self.firstRun > 0) {
        CGPoint point = [[self.positions objectForKey:@(indexPath.item)] CGPointValue];
        attributes.center = CGPointMake(point.x, point.y);
    }

    NSLog(@"END layoutAttributesForItemAtIndexPath. item %d", indexPath.row);
        return attributes;

    }
    @catch(NSException *exception)
    {
        NSLog(@"layoutAttributesForItemAtIndexPath %@: %@",[exception name],[exception reason]);
    }
}

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    @try {

    NSLog(@"layoutAttributesForElementsInRect ...");

    NSMutableArray* attributes = [NSMutableArray array];
    for (NSInteger i=0 ; i < self.cellCount; i++) {
        NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:1];
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:indexPath]];
    }
    NSLog(@"END layoutAttributesForElementsInRect ...");
        return attributes;

    }
    @catch(NSException *exception)
    {
        NSLog(@"layoutAttributesForElementsInRect %@: %@",[exception name],[exception reason]);
    }
}

person Josh Katz    schedule 14.10.2012    source источник
comment
Используемый вами основной класс viewController реализует протокол UICollectionViewDataSource? Вы установили свой viewController (или что-то еще) в качестве источника данных для UICollectionView?   -  person Cashew    schedule 14.10.2012
comment
да. Он реализует 2 протокола: UICollectionViewDelegate и UICollectionViewDataSource. Я установил его как источник данных, а также делегат для двух UICollectionViews на нем.   -  person Josh Katz    schedule 14.10.2012


Ответы (6)


У меня это произошло при запуске reloadData из распознавателя жестов, помогла настройка delaysTouchesBegan в распознавателе жестов.

person Keke    schedule 03.02.2013

Ключевым здесь является этот номер из вашей ошибки:

2147483647

Это числовое значение NSNotFound. Где-то в вашем коде вы делаете что-то вроде indexOfObject: для массива и передаете результат этого в один из ваших методов просмотра коллекции или макета в качестве индекса раздела или элемента, что вызывает ошибку. Этого не происходит в коде, опубликованном в вашем вопросе, но, надеюсь, вы сможете найти его сейчас.

person jrturton    schedule 14.10.2012

Это сработало для меня:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView reloadData];
});
person Hampden123    schedule 06.06.2013
comment
Любые изменения в графическом интерфейсе должны запускаться из основного потока. При изменении или обновлении UICollectionView из блока, задержки или распознавателя жестов вам необходимо отправить его в основную очередь (как описано @Hampden123 выше). - person drpawelo; 14.12.2015

Я только что столкнулся с этим утверждением. Он сработал, потому что я перезагружала данные коллекции в событии касания (пролистывания).

Первоначальное касание (начало) вызвало сообщение didHighlightItemAtIndexPath:, а затем данные коллекции изменились (через пролистывание), а затем не удалось вызвать сообщение didUnhighlightItemAtIndexPath: при касании (подъем).

Поскольку мне не нужна подсветка, решение состояло в том, чтобы реализовать этот метод в моем контроллере:

- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
    return NO;
}

Я надеюсь, что это поможет любому, кто столкнется с этим.

person Daltsy    schedule 09.01.2013
comment
Я просто хотел сказать, что проблема, с которой я столкнулся, заключалась в перезагрузке данных коллекции при длительном касании. Удаление reloadData решило мою проблему. - person J.C.; 02.02.2013

Никогда не перезагружайте данные UICollectionView в методе, выполняемом распознавателем жестов. Следование этому правилу полностью решило эту проблему. Предложение Daltsy деактивировать выделение в UICollectionView также предотвращает ошибку, но также деактивирует все сообщения делегата «didSelectItemAtIndexPath». Будьте осторожны, так как вы можете получить почти бесполезный UICollectionView, как я сделал пару минут назад.

person Andreas Zöllner    schedule 09.02.2013

Вместо отключения подсветки я обнаружил, что проверка того, что ни одна ячейка не выделена перед вызовом reloadData, предотвращает ошибку «запрос количества элементов перед разделом». Это

UICollectionViewController.CollectionView.deselectItemAtIndexPath метод или DeselectItem в Xamarin.

Я сохраняю дескриптор текущего выбранного элемента в данных модели, очищаю выделение представления коллекции, вычисляю новые NSIndexPaths для элементов, а затем вызываю reloadData. Ранее выбранный элемент выбирается снова с помощью дескриптора, чтобы найти его с помощью его возможно нового свойства NSIndexPath.

selectItemAtIndexPath:animated:scrollPosition: или SelectItem в Xamarin.

person Gary Z    schedule 02.12.2015