UICollectionView украшение в пустой коллекции

Я реализовал UICollectionView с настраиваемым макетом. Он добавляет украшение в макет. Я использую следующий код, чтобы добавить атрибуты макета вида украшения:

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

Данные в виде коллекции представлены NSFetchedResultsController.

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

Ответ 1

Я создал и протестировал этот простой пример, который, кажется, работает в iOS 7 во всех возможных ситуациях (0 разделов, 1 раздел с 0 элементами и т.д.). Это мой класс макета, подкласс UICollectionViewFlowLayout. Остальная часть проекта - это просто леса.

#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

Ответ 2

При использовании декоративного вида или дополнительного вида, не привязанного к определенной ячейке, используйте [NSIndexPath indexPathWithIndex:], чтобы указать путь индекса. Вот пример кода:

@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