11
votes

I've implemented an UICollectionView with a custom layout. It adds a decoration view to the layout. I use the following code to add layout attributes of the decoration view:

-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *allAttributes = [super layoutAttributesForElementsInRect:rect];
    return [allAttributes arrayByAddingObject:[self layoutAttributesForDecorationViewOfKind:kHeaderKind atIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]];
}

The data in the collection view is provided by a NSFetchedResultsController.

Now it looked likes it worked fine, but when the collection view is empty, it fails because there's section 0. Tried to use it without an index path, but fails too. Any thoughts on how to use decoration views in an empty UICollectionView? Should be possible since decoration views aren't data-driven.

2
Hi ,have you solved this issue , please update your answer so that we can also get some help. - h.kishan
Can you please post some more code so that i can reproduce the issue easily - Dinesh Kaushik
Do you get the same error message in both cases? Because it works for me when I use nil as the index path (Xcode 5.1.1, iOS SDK 7.1 Simulator). If it fails silently instead, maybe the super call returns nil (UICollectionViewLayouts default)? - Jörn Eyrich

2 Answers

4
votes

When using a decoration view or a supplemental view not attached to a specific cell, use [NSIndexPath indexPathWithIndex:] to specify the index path. Here is a sample code:

@interface BBCollectionViewLayout : UICollectionViewFlowLayout

@end

@implementation BBCollectionViewLayout

- (void)BBCollectionViewLayout_commonInit {
    [self registerClass:[BBCollectionReusableView class] forDecorationViewOfKind:BBCollectionReusableViewKind];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super initWithCoder:aDecoder])) {
        [self BBCollectionViewLayout_commonInit];
    }
    return self;
}

- (id)init {
    self = [super init];
    if (self) {
        [self BBCollectionViewLayout_commonInit];
    }
    return self;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSMutableArray *array = [NSMutableArray arrayWithArray:[super layoutAttributesForElementsInRect:rect]];

    UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForDecorationViewOfKind:BBCollectionReusableViewKind atIndexPath:[NSIndexPath indexPathWithIndex:0]];

    if (CGRectIntersectsRect(rect, attributes.frame)) {
        [array addObject:attributes];
    }

    return array;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString*)elementKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:elementKind withIndexPath:indexPath];
    attributes.frame = CGRectMake(0., 60., 44., 44.);
    return attributes;
}

@end
0
votes

I created and tested this simple example that seems to work in iOS 7 in all possible situations (0 sections, 1 section with 0 items etc). This is my layout class, subclass of UICollectionViewFlowLayout. The rest of the project is just scaffolding.

#import "JKLayout.h"
#import "JKDecoration.h"

@implementation JKLayout

- (instancetype)init
{
    if (self = [super init]) {
        [self registerClass:[JKDecoration class] forDecorationViewOfKind:@"Decoration"];
    }
    return self;
}

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

    // It’s important to set indexPath to nil. If I had set it to indexPath 0-0, it crashed with InternalInconsistencyException
    // because I was trying to get decoration view for section 0 while there in reality was no section 0
    // I guess if you need to have several decoration views in this case, you’d identify them with a method other than indexpath
    return [allAttributes arrayByAddingObject:[self layoutAttributesForDecorationViewOfKind:@"Decoration" atIndexPath:nil]];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes *attr = [super layoutAttributesForDecorationViewOfKind:decorationViewKind atIndexPath:indexPath];
    if (!attr) {
        attr = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
        attr.frame = CGRectMake(0, 200, 100, 100);
    }
    return attr;
}

@end