문제

I have a UICollectionView, the content for which is supplied by an NSFetchedResultsController. Whenever core data is updated, a call is made to update the changed items in the collection view via the reloadItemsAtIndexPaths: method.

After a good amount of testing, I have determined that above process is working correctly. I make updates to core data and ultimately, the datasource method 'cellForItemAtIndexPath' is called and the cells are updated as expected. However, I have also implemented the datasource method 'viewForSupplementaryElementOfKind', to display some cell headers (also based on changes to core data) and this is not working properly.

For some reason, it appears that when 'reloadItemsAtIndexPaths' gets called after the core data changes, 'viewForSupplementaryElementOfKind' does not get called, and I cannot figure out why this would be the case. However, once I start scrolling the collection view, 'viewForSupplementaryElementOfKind' is THEN called, and I am able to see updates in the supplementary header views as I would expect based on the core data changes. This method also gets called successfully upon the initial creation of the UICollectionView.

I have a custom layout that I am using with my collection view, and so perhaps the issue lies in there? I am hoping someone can spot my error.

Here is the code for creating the UICollectionView, its layout, cells and its header views:

  1. Create UICollectionView

            - (void)createCollectionView
            {
                _layout1 = [[BigLayout alloc] init]; //custom layout
                [_layout1 setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    
                _collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:_layout1];
                _collectionView.translatesAutoresizingMaskIntoConstraints = NO;
                _collectionView.backgroundColor = [UIColor clearColor];
    
                _collectionView.dataSource = self;
                _collectionView.delegate = self;
    
                [self.view addSubview:_collectionView];
    
                //set up constraints, add them to view...
    
                [self.collectionView registerClass:[InboxCell class]
                        forCellWithReuseIdentifier:@"inbox"];
                [self.collectionView registerClass:[UICollectionReusableView class]
                        forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
                               withReuseIdentifier:@"header"];
            }
    
  2. Create Custom Layout

        @implementation BigLayout
    
        -(id)init
        {
            self = [super init];
            if (self) {
                self.itemSize = CGSizeMake(330, 588);
                self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
                self.headerReferenceSize = CGSizeMake(13, 13);
            }
            return self;
        }
    
        -(void)prepareLayout
        {
            [super prepareLayout];
    
            _cellCount = [[self collectionView] numberOfItemsInSection:0];
        }
    
        - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path
        {
            UICollectionViewLayoutAttributes* attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:path];
            attributes.size = self.itemSize;
            attributes.center = CGPointMake(path.item * (self.itemSize.width + 20) + self.itemSize.width/2.0 + 20, self.collectionView.center.y);
            return attributes;
        }
    
        - (CGSize)collectionViewContentSize
        {
            return CGSizeMake(((self.itemSize.width + 20) * _cellCount) + 80, [self collectionView].height);
        }
    
        -(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
        {
            NSMutableArray* attributes = [NSMutableArray array];
            for (NSInteger i=0 ; i < self.cellCount; i++) {
                NSIndexPath* indexPath = [NSIndexPath indexPathForItem:i inSection:0];
    
                UICollectionViewLayoutAttributes *attr = [self layoutAttributesForItemAtIndexPath:indexPath];
                UICollectionViewLayoutAttributes *hattr = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
    
                if (CGRectIntersectsRect(attr.frame, rect)) {
                    [attributes addObject:attr];
                    [attributes addObject:hattr];
                }
            }
            return attributes;
        }
    
        - (void)prepareForCollectionViewUpdates:(NSArray *)updateItems
        {
    
        }
    
        - (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath
        {
            UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForItemAtIndexPath:itemIndexPath];
            attributes.alpha = 1.0;
            return attributes;
        }
    
        - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
        {
            UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath];
            attributes.size = CGSizeMake(13, 13);
            attributes.center = CGPointMake(indexPath.item * (self.itemSize.width + 20) + self.itemSize.width/2.0 + 20 + self.collectionView.left, self.collectionView.height == 768 ? 75 : 200);
            attributes.alpha = 1.0f;
            return attributes;
        }
    
  3. DataSource Method for Cells (called on CD updates)

            - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
            {
                //this method gets called on reload items
                InboxCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"inbox" forIndexPath:indexPath];
                Email *email = [self.fetchedResultsController objectAtIndexPath:indexPath];
                //do stuff with the email in the cell
                return cell;
            }
    
  4. Data Source Method for Supplementary Header Views (not called on CD updates)

            - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
                       viewForSupplementaryElementOfKind:(NSString *)kind
                                             atIndexPath:(NSIndexPath *)indexPath
            {
                //this method does not get called on reload items, only gets called initially and on scroll
                UICollectionReusableView *reusableView;
    
                 if (kind == UICollectionElementKindSectionHeader) { //specify in case we add a footer later
    
                     UICollectionReusableView *unreadEmailImageIdentifier = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"header" forIndexPath:indexPath];
                     Email *email = [self.fetchedResultsController objectAtIndexPath:indexPath];
                     UIImageView *unreadEmailDot = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"dot.png"]];
                     [unreadEmailImageIdentifier addSubview:unreadEmailDot];
                     unreadEmailImageIdentifier.hidden = YES;
    
                     if (email.isUnread == YES || email.isUnread == 1) {
                         unreadEmailImageIdentifier.hidden = NO;
                     }
                     reusableView = unreadEmailImageIdentifier;
                 }
    
                return reusableView;
            }
    

In short, it appears that when I attempt to reload the collection view or components of the collection view, the data source method for supplementary header views does not get called. However, I do see that the method to create the attributes for the supplementary view in the custom layout class IS called. If anyone could point out any possible reason this could be happening, I would appreciate it!

도움이 되었습니까?

해결책

reloadItemsAtIndexPaths is only intended to reloads cells. You can reload a supplementary view by reloading the section using reloadSections:. If you still have trouble, please include your in your NSFetchedResultsControllerDelegate implementation in the question.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top