Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Decoration Views added to UICollectionView

I'm creating a basic collection view layout which subclasses UICollectionViewFlowLayout. However, I'm noticing that there appear to be several decoration views stacked on top of one another.

Whenever the user selects an item in the last section, I'm adding a new section with the code below. It seems that whenever I execute this code, one additional copy of the decoration view is being added to each already existing section.

[collectionView performBatchUpdates:^{
    currentModelArrayIndex++;
    [collectionView insertSections:[NSIndexSet indexSetWithIndex:currentModelArrayIndex]];
    [collectionView reloadSections:[NSIndexSet indexSetWithIndex:currentModelArrayIndex-1]];
} completion:^(BOOL finished) {
    [collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:currentModelArrayIndex] atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
}];

I've corroborated this by giving my decoration views an alpha of 0.2f and seeing them stack up.

enter image description here

I also performed a dump of the collection view hierarchy and saw 10 instances of AFDecorationView when I should only see 4:

   | <AFDecorationView: 0x719ee50; baseClass = UICollectionReusableView; frame = (0 65; 768 208.125); alpha = 0; hidden = YES; layer = <CALayer: 0x719eec0>>
   | <AFDecorationView: 0x71ad980; baseClass = UICollectionReusableView; frame = (0 333.125; 768 203.281); alpha = 0; hidden = YES; layer = <CALayer: 0x71adb60>>
   | <AFDecorationView: 0x71afc90; baseClass = UICollectionReusableView; frame = (0 65; 768 208.125); layer = <CALayer: 0x71afd60>>
   | <AFDecorationView: 0xd79ac30; baseClass = UICollectionReusableView; frame = (0 596.406; 768 203.281); alpha = 0; hidden = YES; layer = <CALayer: 0xd79ad00>>
   | <AFDecorationView: 0xd79cf20; baseClass = UICollectionReusableView; frame = (0 333.125; 768 203.281); layer = <CALayer: 0xd79cff0>>
   | <AFDecorationView: 0xd79dac0; baseClass = UICollectionReusableView; frame = (0 65; 768 208.125); layer = <CALayer: 0xd79a980>>
   | <AFDecorationView: 0xd794fd0; baseClass = UICollectionReusableView; frame = (0 859.688; 768 225.938); layer = <CALayer: 0xd7950a0>>
   | <AFDecorationView: 0xd7a1300; baseClass = UICollectionReusableView; frame = (0 596.406; 768 203.281); layer = <CALayer: 0xd7a13d0>></CALayer:>
   | <AFDecorationView: 0xd7a35d0; baseClass = UICollectionReusableView; frame = (0 65; 768 208.125); layer = <CALayer: 0xd794470>>
   | <AFDecorationView: 0xd7a43e0; baseClass = UICollectionReusableView; frame = (0 333.125; 768 203.281); layer = <CALayer: 0xd7a44b0>>

I have tried looking at another custom layout example, but they seem to instantiate quite a few instances of their decoration views, too. Could this be a bug in UICollecionView? Or is it up to us to only add the decoration view layout attribute in layoutAttributesForElementsInRect: once per section?

The decoration-related parts of custom layout look like the following.

@implementation AFCollectionViewFlowLayout

-(id)init
{
    if (!(self = [super init])) return nil;

    [self registerClass:[AFDecorationView class] forDecorationViewOfKind:AFCollectionViewFlowLayoutBackgroundDecoration];

    return self;
}

#pragma mark - Private Helper Methods

-(void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)attributes
{
    //implemented
}

#pragma mark - Overridden Methods

#pragma mark Cell Layout

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *attributesArray = [super layoutAttributesForElementsInRect:rect];

    NSMutableArray *newAttributesArray = [NSMutableArray array];

    for (UICollectionViewLayoutAttributes *attributes in attributesArray)
    {
        [self applyLayoutAttributes:attributes];

        if (attributes.indexPath.item == 0)
        {
            UICollectionViewLayoutAttributes *newAttributes = [self layoutAttributesForDecorationViewOfKind:AFCollectionViewFlowLayoutBackgroundDecoration atIndexPath:attributes.indexPath];

            [newAttributesArray addObject:newAttributes];
        }
    }

    attributesArray = [attributesArray arrayByAddingObjectsFromArray:newAttributesArray];

    return attributesArray;
}

#pragma mark Decoration View Layout

-(UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    if ([decorationViewKind isEqualToString:AFCollectionViewFlowLayoutBackgroundDecoration])
    {
        UICollectionViewLayoutAttributes *tallestCellAttributes;
        NSInteger numberOfCellsInSection = [self.collectionView numberOfItemsInSection:indexPath.section];

        for (NSInteger i = 0; i < numberOfCellsInSection; i++)
        {
            NSIndexPath *cellIndexPath = [NSIndexPath indexPathForItem:i inSection:indexPath.section];

            UICollectionViewLayoutAttributes *cellAttribtes = [self layoutAttributesForItemAtIndexPath:cellIndexPath];

            if (CGRectGetHeight(cellAttribtes.frame) > CGRectGetHeight(tallestCellAttributes.frame))
            {
                tallestCellAttributes = cellAttribtes;
            }
        }

        CGFloat decorationViewHeight = CGRectGetHeight(tallestCellAttributes.frame) + self.headerReferenceSize.height;

        layoutAttributes.size = CGSizeMake([self collectionViewContentSize].width, decorationViewHeight);
        layoutAttributes.center = CGPointMake([self collectionViewContentSize].width / 2.0f, tallestCellAttributes.center.y);
        layoutAttributes.zIndex = -1;
    }

    return layoutAttributes;
}

@end

I only want one decoration view per section, so I'm adding the decoration view to the first section.

like image 349
Ash Furrow Avatar asked Jan 13 '13 19:01

Ash Furrow


2 Answers

This appears to be a bug in UICollectionView. I've filed a bug report with Apple.

like image 119
Ash Furrow Avatar answered Sep 21 '22 11:09

Ash Furrow


I had a similar issue but I was able to solve it by manually removing all decoration views in prepareLayout in my custom layout class (I do that right before I invoke [super prepareLayout]):

    // Iterate over all subviews to remove all decoration views 
    for (UIView *view in self.collectionView.subviews) 
    {
        if ([view isKindOfClass:[DecorationView class]])
        {
            [view removeFromSuperview];
        }

    }
    [super prepareLayout];
like image 36
nburk Avatar answered Sep 22 '22 11:09

nburk