滚动时,大型UICollectionViewCell停止显示

时间:2013-01-10 09:10:07

标签: iphone ios uicollectionview

此处所述UICollectionView的相同行为已导致this question。即使我决定发布我自己的,因为我做了进一步的调查,我不想发表评论或编辑上述问题。

会发生什么?:

当大型单元格在UICollectionView中显示UICollectionViewFlowLayout时,在将集合视图滚动到某个偏移量后,单元格将消失。

当进一步滚动直到另一个单元格进入可见区域时,消失/隐藏的单元格再次变为可见。

我使用垂直滚动集合视图和全宽度单元格进行了测试,但我很确定,水平滚动的类似设置也会发生这种情况。

什么是大型细胞?:

所述行为发生在单元格高于显示高度两倍的情况下(3,5英寸显示屏为960.f + 1.f,{4}英寸为1136.f + 1.f

完全发生了什么?:

当集合视图的滚动偏移超过cell.frame.origin.y + displayHeightOfHardware时,单元格隐藏属性设置为YES并且-collectionView:didEndDisplayingCell:forItemAtIndexPath:被调用(例如,{{1}时第一个单元格变为隐藏在3,5英寸的iPhone上达到scrollingOffset.y

如上所述,当滚动直到下一个单元格进入视图时,隐藏的单元格再次显示(即隐藏属性更改为481.f),此外,当滚动得足够远时,单元格将永远不会再次消失,当它不管怎么说都不应该滚动。

使用大于三显示高度(NO)的单元格时会发生这种情况。那些表现出相同的行为,但它们保持不变,无论它们上下滚动多远。

还有什么?:

无法通过覆盖1441.f/1705.f来返回-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds来解决这种情况。

在隐藏属性隐藏后(例如YES中)隐藏属性设置为NO,无法强制显示单元格

那么,问题是什么?:

我很确定,这是didEndDisplayingCell中的错误,我将在Apple提交TSI。但与此同时:有没有任何关于快速解决方案的想法

4 个答案:

答案 0 :(得分:9)

我有一个非常脏的内部解决方案来解决这个问题:

@interface UICollectionView ()
- (CGRect)_visibleBounds;
@end

@interface MyCollectionView : UICollectionView

@end

@implementation MyCollectionView

- (CGRect)_visibleBounds {
    CGRect rect = [super _visibleBounds];
    rect.size.height = [self heightOfLargestVisibleCell];
    return rect;
}

- (float)heightOfLargestVisibleCell {
    // do your calculations for current max cellHeight and return it 
    return 1234;
}

@end

答案 1 :(得分:3)

我有一个似乎对我有用的解决方法,不应该像Apple的iOS应用程序规则那样。

关键是观察到大细胞界限是问题。我通过确保单元格的一个边缘位于可滚动内容区域的可视区域内来解决这个问题。您显然需要根据需要对UICollectionViewFlowLayout类或UICollectionViewLayout进行子类化,并使用contentOffset值来跟踪您在UIScrollView中的位置。

我还必须确保:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 

返回YES或面临运行时异常,指示布局无效。在我的情况下,我保持较大单元格的边缘绑定到左边缘。这样,您可以避免这些较大单元格的错误边界交叉检测。

这会创建更多工作,具体取决于您在滚动时更新单元格的宽度/高度时要呈现单元格的内容的方式。在我的例子中,单元格中的子视图相对简单,不需要大量的摆弄。

根据此处的要求,我的layoutAttributesInRect

就是一个例子
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSMutableArray* attributes = [NSMutableArray array];
    NSArray *vertical = myVerticalCellsStore.cells;
    NSInteger startRow = floor(rect.origin.y * (vertical.count)/ (vertical.count * verticalViewHeight + verticalViewSpacing * 2));
    startRow = (startRow < 0) ? 0 : startRow;

    for (NSInteger i = startRow; i < vertical.count && (rect.origin.y + rect.size.height >= i * verticalViewHeight); i++) {
        NSArray *horizontals = myHorizontalStore.horizontalCells;
        UICollectionViewLayoutAttributes *verticalAttr = [self layoutAttributesForSupplementaryViewOfKind:@"vertical" atIndexPath:[NSIndexPath indexPathForItem:0 inSection:i]];
        if (CGRectIntersectsRect(verticalAttr.frame, rect)) {
            [attributes addObject:verticalAttr];
        }

        BOOL foundAnElement = NO;
        for (NSInteger j = 0 ; j < horizontals.count; j++) {
            MYViewLayoutAttributes *attr = (MyViewLayoutAttributes *)[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]];
            if (CGRectIntersectsRect(rect, attr.frame)) {
                [attributes addObject: attr];
                foundAnElement = YES;
            }
            else if (foundAnElement) {
                break;
            }
        }
    }
    return attributes;
}

这是我的清理代码。基本上我计算的是第一个细胞应该基于细胞高度。在我修复的情况下,计算非常简单。但我的水平元素有各种宽度。因此内部循环实际上是要确定要包含在属性数组中的正确数量的水平单元格。在那里我使用CGRectIntersectsRect来确定细胞是否相交。然后循环继续前进,直到交叉点失败。如果找到至少一个水平单元,则循环将断开。希望有所帮助。

答案 2 :(得分:0)

我的解决方案与Jonathan的解决方案基本相同,但在类别中,因此您不必使用自己的子类。

@implementation UICollectionView (MTDFixDisappearingCellBug)

+ (void)load {
    NSError *error = nil;
    NSString *visibleBoundsSelector = [NSString stringWithFormat:@"%@isib%@unds", @"_v",@"leBo"];

    if (![[self class] swizzleMethod:NSSelectorFromString(visibleBoundsSelector) withMethod:@selector(mtd_visibleBounds) error:&error]) {
        FKLogErrorVariables(error);
    }
}

- (CGRect)mtd_visibleBounds {
    CGRect bounds = [self mtd_visibleBounds]; // swizzled, no infinite loop
    MTDDiscussCollectionViewLayout *layout = [MTDDiscussCollectionViewLayout castedObjectOrNil:self.collectionViewLayout];

    // Don`t ask me why, but there's a visual glitch when the collection view is scrolled to the top and the max height is too big,
    // this fixes it
    if (bounds.origin.y <= 0.f) {
        return bounds;
    }

    bounds.size.height = MAX(bounds.size.height, layout.maxColumnHeight);

    return bounds;
}

@end

答案 3 :(得分:0)

我发现只有在使用子类UICollectionViewLayoutAttributes并且该属性类没有正确的isEqual:方法时才会出现此问题。

例如:

@implementation COGridCollectionViewLayoutAttributes
- (id)copyWithZone:(NSZone *)zone
{
    COGridCollectionViewLayoutAttributes *attributes = [super copyWithZone:zone];
    attributes.isInEditMode = _isInEditMode;
    return attributes;
}

- (BOOL)isEqual:(id)other {
    if (other == self) {
        return YES;
    }
    if (!other || ![[other class] isEqual:[self class]]) {
        return NO;
    }
    if ([((COGridCollectionViewLayoutAttributes *) other) isInEditMode] != [self isInEditMode]) {
        return NO;
    }

    return [super isEqual:other];
}

@end

工作但最初我有:

return YES;

这是在iOS 7上。

相关问题