Подтвердить что ты не робот

Большой UICollectionViewCell перестает отображаться при прокрутке

То же поведение UICollectionView, как описано здесь, было вызвано этим вопросом. Несмотря на то, что я решил опубликовать свой собственный, потому что я провел дальнейшие расследования, я не хотел публиковать в комментарии или редактировать вопрос, упомянутый выше.

Что происходит?:

Когда большие ячейки отображаются в UICollectionView с UICollectionViewFlowLayout, после прокрутки представления коллекции до определенного смещения ячейки исчезнут.

При прокрутке дальше, пока другая камера не попадет в видимую область, исчезающая/скрытая ячейка снова станет видимой.

Я тестировал с вертикальным списком коллекции прокрутки и полноразмерными ячейками, но я уверен, что это также произойдет с аналогичными настройками для горизонтальной прокрутки.

Что такое большие ячейки?:

Описанное поведение происходит с ячейками, превышающими вдвое больше высоты дисплея (960.f + 1.f на 3,5-дюймовых дисплеях, 1136.f + 1.f на 4 дюйма).

Что именно происходит?:

Когда смещение прокрутки в представлении коллекции превышает cell.frame.origin.y + displayHeightOfHardware, свойство скрытых ячеек установлено на YES и вызывает вызов -collectionView:didEndDisplayingCell:forItemAtIndexPath: (например, первая ячейка изменяется на скрытую, когда scrollingOffset.y достигает 481.f на 3, 5-дюймовый iPhone).

Как описано выше, при прокрутке до появления следующей ячейки скрытая ячейка снова отображается (т.е. скрытое свойство изменяется на NO) и, кроме того, при прокрутке достаточно далеко ячейка никогда не исчезнет снова, t, независимо от того, где вы прокручиваете.

Это изменяется при работе с ячейками, большими, чем triple-display-height (1441.f/1705.f). Они показывают одно и то же поведение, но оно остается неизменным, независимо от того, насколько далеко они прокручиваются вверх и вниз.

Что еще?:

Ситуация не может быть исправлена ​​путем переопределения -(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds для возврата YES.

Ячейки не могут быть принудительно отображены с установкой скрытого свойства на NO программно после их скрытия (например, в didEndDisplayingCell)

Итак, в чем вопрос?:

Я уверен, что это ошибка в UICollectionView/Controller/Cell/Layout, и я отправлю TSI в Apple. Но тем временем: Есть ли у кого-нибудь идеи для быстрого решения hack?

4b9b3361

Ответ 1

У меня есть ОЧЕНЬ грязное и внутреннее решение для этой проблемы:

@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

Ответ 2

У меня есть обходное решение, которое, похоже, работает для меня и не должно запускать правила Apple для приложений iOS.

Ключ является наблюдением, что проблема связана с большими ячейками. Я работал над этим, гарантируя, что один край ячейки находится в пределах видимой области области прокручиваемого содержимого. Очевидно, что вам нужно подклассифицировать класс UICollectionViewFlowLayout или UICollectionViewLayout в зависимости от ваших потребностей и использовать значение contentOffset для отслеживания, где вы находитесь в UIScrollView.

Я также должен был обеспечить:

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds 

возвращает YES или сталкивается с исключением среды выполнения, указывающим, что макет был недействителен. Я держу край большей ячейки привязанным к левому краю в моем случае. Таким образом, вы можете избежать обнаружения ошибочных границ пересечения для этих более крупных ячеек.

Это создает больше работы в зависимости от того, как вы хотите, чтобы содержимое ячейки отображалось, когда ширина/высота ячейки обновляется по мере прокрутки. В моем случае, subviews внутри ячейки относительно просты и не требуют много возиться с.

В соответствии с просьбой приведен пример моего 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, чтобы определить, пересекается ли ячейка. Затем цикл продолжается, пока пересечение не сработает. И если хотя бы одна горизонтальная ячейка найдена, петля сломается. Надеюсь, что это поможет.

Ответ 3

Мое решение в основном такое же, как у Джонатана, но в категории, поэтому вам не нужно использовать свой собственный подкласс.

@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 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

Ответ 4

Я обнаружил, что эта проблема возникла только при использовании подкласса 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.