19

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

怎么了?:

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

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

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

什么是大细胞?:

所描述的行为发生在高于显示高度两倍的单元格(960.f + 1.f在 3,5 英寸显示器上,1136.f + 1.f在 4 英寸上)。

究竟会发生什么?:

当集合视图的滚动偏移量超过cell.frame.origin.y + displayHeightOfHardware单元格隐藏属性设置为YES并被调用(例如,当到达3.5 英寸 iPhone-collectionView:didEndDisplayingCell:forItemAtIndexPath:时,第一个单元格变为隐藏)。scrollingOffset.y481.f

如上所述,当滚动到下一个单元格进入视图时,隐藏单元格会再次显示(即隐藏属性更改为NO),此外,当滚动足够远时,单元格将永远不会再次消失,而它不应该再次消失,无论你在哪里滚动到。

当使用大于 Triple-display-height ( 1441.f/1705.f) 的单元格时,这种情况会发生变化。这些显示相同的行为,但无论它们向上和向下滚动多远,它都保持不变。

还有什么?:

这种情况不能通过覆盖-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds返回来解决YES

单元格在隐藏后无法通过以NO编程方式设置隐藏属性来强制显示(didEndDisplayingCell例如)

那么,问题是什么?:

我很确定,这是一个错误,UICollectionView/Controller/Cell/Layout我将在 Apple 提交 TSI。但与此同时:有没有人对快速破解解决方案有任何想法

4

4 回答 4

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
于 2013-01-10T13:58:55.327 回答
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来确定单元格是否相交。然后循环继续进行,直到交叉点失败。如果至少找到一个水平单元格,则循环将中断。希望有帮助。

于 2013-02-08T15:45:02.933 回答
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 上。

于 2013-10-16T23:00:06.943 回答
0

我的解决方案与乔纳森的解决方案基本相同,但属于一个类别,因此您不必使用自己的子类。

@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
于 2013-05-23T18:43:03.810 回答