Как добавить представления между UICollectionViewCells в UICollectionView?

Я пытаюсь добавить UIViews между моими UICollectionViewCells в моем UICollectionView, и я не знаю, как я могу это сделать. Я пытаюсь выполнить что-то вроде этого:

Снимок экрана

Мне, вероятно, нужно будет написать собственный UICollectionViewLayout, но я действительно не знаю, с чего начать.


person Marco Pompei    schedule 01.04.2013    source источник


Ответы (4)


Я больше изучил, как работают UICollectionViewLayout, и понял, как это решить. У меня есть подкласс UICollectionReusableView с именем OrangeView, который будет расположен между моими представлениями, затем я написал подкласс UICollectionViewFlowLayout с именем CategoriesLayout, который будет иметь дело с моим макетом.

Извините за большой блок кода, но вот как это выглядит:

@implementation CategoriesLayout

- (void)prepareLayout {
    // Registers my decoration views.
    [self registerClass:[OrangeView class] forDecorationViewOfKind:@"Vertical"];
    [self registerClass:[OrangeView class] forDecorationViewOfKind:@"Horizontal"];
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    // Prepare some variables.
    NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];

    UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];

    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];

    CGRect baseFrame = cellAttributes.frame;
    CGRect nextFrame = nextCellAttributes.frame;

    CGFloat strokeWidth = 4;
    CGFloat spaceToNextItem = 0;
    if (nextFrame.origin.y == baseFrame.origin.y)
        spaceToNextItem = (nextFrame.origin.x - baseFrame.origin.x - baseFrame.size.width);

    if ([decorationViewKind isEqualToString:@"Vertical"]) {
        CGFloat padding = 10;

        // Positions the vertical line for this item.
        CGFloat x = baseFrame.origin.x + baseFrame.size.width + (spaceToNextItem - strokeWidth)/2;
        layoutAttributes.frame = CGRectMake(x,
                                            baseFrame.origin.y + padding,
                                            strokeWidth,
                                            baseFrame.size.height - padding*2);
    } else {
        // Positions the horizontal line for this item.
        layoutAttributes.frame = CGRectMake(baseFrame.origin.x,
                                            baseFrame.origin.y + baseFrame.size.height,
                                            baseFrame.size.width + spaceToNextItem,
                                            strokeWidth);
    }

    layoutAttributes.zIndex = -1;
    return layoutAttributes;
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *baseLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray * layoutAttributes = [baseLayoutAttributes mutableCopy];

    for (UICollectionViewLayoutAttributes *thisLayoutItem in baseLayoutAttributes) {
        if (thisLayoutItem.representedElementCategory == UICollectionElementCategoryCell) {
            // Adds vertical lines when the item isn't the last in a section or in line.
            if (!([self indexPathLastInSection:thisLayoutItem.indexPath] ||
                  [self indexPathLastInLine:thisLayoutItem.indexPath])) {
                UICollectionViewLayoutAttributes *newLayoutItem = [self layoutAttributesForDecorationViewOfKind:@"Vertical" atIndexPath:thisLayoutItem.indexPath];
                [layoutAttributes addObject:newLayoutItem];
            }

            // Adds horizontal lines when the item isn't in the last line.
            if (![self indexPathInLastLine:thisLayoutItem.indexPath]) {
                UICollectionViewLayoutAttributes *newHorizontalLayoutItem = [self layoutAttributesForDecorationViewOfKind:@"Horizontal" atIndexPath:thisLayoutItem.indexPath];
                [layoutAttributes addObject:newHorizontalLayoutItem];
            }
        }
    }

    return layoutAttributes;
}

@end

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

@implementation UICollectionViewFlowLayout (Helpers)

- (BOOL)indexPathLastInSection:(NSIndexPath *)indexPath {
    NSInteger lastItem = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
    return  lastItem == indexPath.row;
}

- (BOOL)indexPathInLastLine:(NSIndexPath *)indexPath {
    NSInteger lastItemRow = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:indexPath.section] -1;
    NSIndexPath *lastItem = [NSIndexPath indexPathForItem:lastItemRow inSection:indexPath.section];
    UICollectionViewLayoutAttributes *lastItemAttributes = [self layoutAttributesForItemAtIndexPath:lastItem];
    UICollectionViewLayoutAttributes *thisItemAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];

    return lastItemAttributes.frame.origin.y == thisItemAttributes.frame.origin.y;
}

- (BOOL)indexPathLastInLine:(NSIndexPath *)indexPath {
    NSIndexPath *nextIndexPath = [NSIndexPath indexPathForItem:indexPath.row+1 inSection:indexPath.section];

    UICollectionViewLayoutAttributes *cellAttributes = [self layoutAttributesForItemAtIndexPath:indexPath];
    UICollectionViewLayoutAttributes *nextCellAttributes = [self layoutAttributesForItemAtIndexPath:nextIndexPath];

    return !(cellAttributes.frame.origin.y == nextCellAttributes.frame.origin.y);
}

@end

И это окончательный результат:

Окончательный результат

person Marco Pompei    schedule 03.04.2013
comment
Когда ячейка удаляется снизу, этот код не удаляет разделитель, есть ли способ сделать это? - person Weston; 24.02.2014
comment
@Kronusdark попробуйте вызвать -invalidateLayout в макете представления коллекции, чтобы узнать, заставляет ли он перерисовывать все. Не проверял, не уверен, что работает... - person Marco Pompei; 24.02.2014
comment
Это работает, но зависает на секунду, думаю, мне придется поиграть с таймингом. Большое спасибо за быстрый ответ - person Weston; 24.02.2014
comment
@Kronusdark Да, я думал, что это может случиться. Я проверил справочник по классу UICollectionViewLayout. , проверьте Methods to Override, шесть из них относятся к добавлению и удалению элементов, их реализация должна работать. Возможно, я попытаюсь обновить этот код, когда вернусь домой, но я не могу вам ничего обещать. . - person Marco Pompei; 24.02.2014
comment
Слишком сложно понять, если есть ссылка на скачивание исходного проекта, будет лучше - person Gank; 05.11.2014
comment
@MarcoPompei Почему я не вижу линий? Мой OrangeView : UICollectionReusableView, если пустой класс. - person Gank; 05.11.2014

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

Но, судя по вашей иллюстрации, вам просто нужно определить UICollectionViewCell, чтобы он содержал эти представления. Итак, где вы регистрируете свой класс:

[collectionView registerClass:[CollectionViewCell class] forCellWithReuseIdentifier:@"Cell"];

поместите эти изображения границ в подкласс ячеек UICollectionView (в приведенном выше случае «CollectionViewCell»). Это кажется самым простым подходом.

Вот один из них, который я использую:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.restorationIdentifier = @"Cell";
        self.backgroundColor = [UIColor clearColor];
        self.autoresizingMask = UIViewAutoresizingNone;
        const CGFloat borderWidth = 3.0f;
        UIView *bgView = [[UIView alloc] initWithFrame:frame];
        bgView.layer.borderColor = [UIColor blackColor].CGColor;
        bgView.layer.borderWidth = borderWidth;
        bgView.layer.cornerRadius = 6.0f;
        self.selectedBackgroundView = bgView;
    }
    return self;
}
person RegularExpression    schedule 01.04.2013
comment
Разделы не присвоены. Мне нужно знать такие вещи, как, если это последняя ячейка в этой строке, я не должен отображать строку справа. Я хочу представление коллекции, которое можно было бы вращать и, возможно, даже повторно использовать на iPad, где расстояние между ячейками было бы другим. - person Marco Pompei; 01.04.2013
comment
Вы можете управлять расстоянием и размером макетов (независимо от целевого устройства или ориентации) с помощью методов протокола UICollectionViewDelegateFlowLayout. Если вы правильно структурируете ячейку, нет никаких причин, по которым вы не можете управлять представлениями границ любым удобным для вас способом на основе indexPath ячейки. - person RegularExpression; 02.04.2013

Похоже, если ваш фон collectionView был зеленым, а contentView белым, вы могли бы получить горизонтали с пробелом между ячейками минимальный межстрочный интервал. Вертикальный зазор был бы сложной частью, но если бы вы проявили творческий подход к своему contentView и установили minimumInteritemSpacing, чтобы вы могли его получить.

person Samuel    schedule 03.04.2013

Ух ты. В других ответах много кода только для разделительной строки между строками.

Вот как я это решил. Сначала вам нужно добавить разделитель строк внутри ячейки. Убедитесь, что вы продолжаете перетаскивать его, делая его шире, чем фактическая ширина ячейки, поэтому, если ваша ширина ячейки составляет 60p, ваша разделительная линия будет 70.

@implementation CollectionViewController
{
    NSArray *test;
    int currentLocInRow;
}

внутри cellForItemAtIndexPath:

if((indexPath.row+1) % 4 == 0)
    {
        cell.clipsToBounds = YES;
    }
    else
    {
        cell.clipsToBounds = NO;
    }
    currentLocInRow++;

    if([test count] - indexPath.row+1 < 4 - currentLocInRow)
    {
        cell.lineSeparator.alpha = 0;
    }
    else
    {
        cell.lineSeparator.alpha = 1;
    }
    if(currentLocInRow==4)currentLocInRow=0;

введите здесь описание изображения

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

person Segev    schedule 10.12.2013
comment
Самая сложная часть — получить разделители между элементами внутри одной строки и заставить их работать независимо от размера контейнера (при повороте или на iPad). - person Marco Pompei; 11.12.2013
comment
@MarcoPompei Мне нужно было только реализовать линии под строками для приложения, над которым я работал. Если мне когда-нибудь понадобится добавить разделители между элементами, я опубликую свое решение здесь (если я найду лучшее ..) Отличный вопрос. - person Segev; 11.12.2013
comment
Хотя это меньше кода, это не то, что следует поощрять. Очевидно, это хак, который решает только 50% проблемы. - person raulriera; 07.01.2016