空集合视图中的 UICollectionView 装饰

Posted

技术标签:

【中文标题】空集合视图中的 UICollectionView 装饰【英文标题】:UICollectionView decoration in empty collection view 【发布时间】:2012-10-12 13:27:59 【问题描述】:

我已经实现了一个带有自定义布局的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 中使用装饰视图有什么想法吗?应该可以,因为装饰视图不是数据驱动的。

【问题讨论】:

您好,您解决了这个问题吗,请更新您的答案,以便我们也可以得到一些帮助。 能否请您发布更多代码,以便我可以轻松重现该问题 在这两种情况下你得到相同的错误信息吗?因为当我使用nil 作为索引路径(Xcode 5.1.1,ios SDK 7.1 Simulator)时,它对我有用。如果它静默失败,也许super 调用会返回nilUICollectionViewLayouts 默认)? 【参考方案1】:

当使用装饰视图或未附加到特定单元格的补充视图时,使用[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

【讨论】:

是的,使用不基于部分/项目的 NSIndexPath 似乎有效。不过让我有点不舒服——我认为这些 API 中的大多数都假定 indexPath 与 section.item 格式匹配,尽管这种特定用途似乎没有任何不良影响。 @Jaanus 如果 nil 是一个可接受的值,我认为它会在文档中提及。由于索引路径未绑定到特定项目,因此不使用 section + item 创建索引路径是正常的。【参考方案2】:

我创建并测试了这个简单的示例,它似乎在所有可能的情况下(0 个部分、1 个包含 0 个项目的部分等)都适用于 iOS 7。这是我的布局类,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

【讨论】:

不确定使用nil 作为索引路径是否改变了事情,但使用上面的代码确实可以完美运行。也有可能当时没有用,UICollectionViews 当时有点儿马车。 是的,可能是以上所有的组合。文档没有提供关于如何处理 NSIndexPath 装饰视图的太多指导,只是说“由您决定如何使用 indexPath 参数来识别给定的装饰视图”。 不幸的是,这不再起作用了,因为 layoutAttributesForDecorationViewOfKind:indexPath: 需要一个 nonnull 索引路径。

以上是关于空集合视图中的 UICollectionView 装饰的主要内容,如果未能解决你的问题,请参考以下文章

一个 UIViewController 中的两个 UICollectionView

UICollectionView reloadData() 不会更新集合视图中的单元格

UICollectionView 显示空单元格

UITableView中的UICollectionView。如何在集合视图变高时告诉tableview单元格改变高度?

UITableViewCell 中的 UICollectionView 重用

以编程方式禁用 UICollectionView 中的垂直滚动