多个装饰视图添加到 UICollectionView

Posted

技术标签:

【中文标题】多个装饰视图添加到 UICollectionView【英文标题】:Multiple Decoration Views added to UICollectionView 【发布时间】:2013-01-13 19:46:39 【问题描述】:

我正在创建一个基本的集合视图布局,它是UICollectionViewFlowLayout 的子类。但是,我注意到似乎有几个装饰视图堆叠在一起。

每当用户在最后一个部分中选择一个项目时,我都会使用下面的代码添加一个新部分。似乎每当我执行此代码时,都会向每个已存在的部分添加一个额外的装饰视图副本。

[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];
];

我通过给我的装饰视图一个alpha0.2f 并看到它们堆叠来证实了这一点。

我还执行了集合视图层次结构的转储,并看到了 10 个 AFDecorationView 实例,而我应该只看到 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>>

我试过查看another custom layout example,但他们似乎也实例化了很多装饰视图实例。这可能是UICollecionView 中的错误吗?还是我们只在layoutAttributesForElementsInRect: 中添加装饰视图布局属性每个部分一次?

自定义布局的装饰相关部分如下所示。

@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

我只希望每个部分有一个装饰视图,所以我将装饰视图添加到第一部分。

【问题讨论】:

我还在考虑这个问题,但我有 2 个 cmets:(a) 你能检查一下你的 if (attributes.indexPath.item == 0) 有多少次是真的吗?也许还有其他观点,比如其他补充观点,导致它比你预期的更火? (b) 使用给定部分中第 0 行单元格的存在来确定是否应该包含装饰单元格似乎有点有趣,因为它们(我假设)占据不同的矩形,所以对 layoutAttributesForElementsInRect: 的一些调用应该可能会返回一个而不是另一个。 另外:如果您只使用 reloadData 而不是批量更新,行为会改变吗? 前段时间我也遇到了收藏视图问题。在尝试修复它很多小时后,我提交了一份 TSI。苹果工程师告诉我这是一个错误。它似乎很麻烦...... @JesseRusak 感谢您的建议 - 我会尝试看看您是否正确。你能为indexPath.item == 0 推荐一个替代方案吗?我的装饰视图是每个部分一个。谢谢! 我已将我的支票更改为attributes.representedElementCategory == UICollectionElementCategorySupplementaryView 并添加了日志以查看它是否被多次添加,但看起来很正常。我认为 GuidoH 是对的 - 这只是马车。 【参考方案1】:

这似乎是UICollectionView 中的一个错误。我已经向 Apple 提交了bug report。

【讨论】:

是的,我认为你是对的。我有一个类似的问题,其中在内容更新后使用了错误类型的装饰视图,而现有的装饰视图没有被删除。这不是我认为我在 UICollectionView 中看到的唯一错误。希望它会在下一个版本中进行排序。 有类似的问题。 UICollectionView 不是生产就绪恕我直言。错误太多,尤其是装饰/补充视图。苹果真的用这个丢球了 你收到苹果关于这个错误的回复了吗?我在补充视图堆积时遇到了类似的问题 - 这次是我 invalidateLayout。额外的视图永远不会被删除,当我的布局移动时,这会导致真正的问题。 这已从 ios 开始解决[已编辑] - 目前没有其他追索权。 我在 iOS 8.1.3 上遇到了这个错误。就我而言,如果我以编程方式设置contentOffset,就会发生这种情况。【参考方案2】:

我遇到了类似的问题,但我能够通过在我的自定义布局类中手动删除 prepareLayout 中的所有装饰视图来解决它(我在调用 [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];

【讨论】:

以上是关于多个装饰视图添加到 UICollectionView的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewLayout

Flask--视图增加多装饰器

Django中视图的多个装饰器:执行顺序

如何使用装饰器将多个 div 或字段集添加到 zend_form?

116.类视图添加装饰器

我可以通过 django 视图访问装饰器中的上下文对象吗