UICollectionView自定义行分隔符

Ant*_*nko 19 objective-c ios uicollectionview uicollectionviewlayout uicollectionreusableview

我想使二点黑色隔板UICollectionView为我们的新的应用程序.我们的应用程序截图如下.我们无法使用UITableView,因为我们有自定义插入/删除动画,滚动和视差效果等.

例

Ant*_*nko 43

我从三个想法开始如何制作它:

  • 在细胞内部实施这些分离器
  • 使用纯黑色的背景minimumLineSpacing,因此我们将看到背景细胞之间的空间
  • 使用自定义布局并将此分隔符实现为装饰

前两个变体被拒绝,因为意识形态不一致,自定义动画和收集下面的内容.我也已经有了自定义布局.

我将用自定义子类描述这些步骤UICollectionViewFlowLayout.

--1--

实现自定义UICollectionReusableView子类.

@interface FLCollectionSeparator : UICollectionReusableView

@end

@implementation FLCollectionSeparator

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor blackColor];
    }

    return self;
}

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
    self.frame = layoutAttributes.frame;
}

@end
Run Code Online (Sandbox Code Playgroud)

--2--

说布局使用自定义装饰.还要在单元格之间建立行间距.

UICollectionViewFlowLayout* layout = (UICollectionViewFlowLayout*) self.newsCollection.collectionViewLayout;
[layout registerClass:[FLCollectionSeparator class] forDecorationViewOfKind:@"Separator"];
layout.minimumLineSpacing = 2;
Run Code Online (Sandbox Code Playgroud)

--3--

在自定义UICollectionViewFlowLayout子类中,我们应该UICollectionViewLayoutAttributes从中返回装饰layoutAttributesForElementsInRect.

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    ... collect here layout attributes for cells ... 

    NSMutableArray *decorationAttributes = [NSMutableArray array];
    NSArray *visibleIndexPaths = [self indexPathsOfSeparatorsInRect:rect]; // will implement below

    for (NSIndexPath *indexPath in visibleIndexPaths) {
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForDecorationViewOfKind:@"Separator" atIndexPath:indexPath];
        [decorationAttributes addObject:attributes];
    }

    return [layoutAttributesArray arrayByAddingObjectsFromArray:decorationAttributes];
}
Run Code Online (Sandbox Code Playgroud)

--4--

对于可见的rect,我们应该返回可见的装饰索引.

- (NSArray*)indexPathsOfSeparatorsInRect:(CGRect)rect {
    NSInteger firstCellIndexToShow = floorf(rect.origin.y / self.itemSize.height);
    NSInteger lastCellIndexToShow = floorf((rect.origin.y + CGRectGetHeight(rect)) / self.itemSize.height);
    NSInteger countOfItems = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:0];

    NSMutableArray* indexPaths = [NSMutableArray new];
    for (int i = MAX(firstCellIndexToShow, 0); i <= lastCellIndexToShow; i++) {
        if (i < countOfItems) {
            [indexPaths addObject:[NSIndexPath indexPathForRow:i inSection:0]];
        }
    }
    return indexPaths;
}
Run Code Online (Sandbox Code Playgroud)

--5--

我们也应该实施layoutAttributesForDecorationViewOfKind.

- (UICollectionViewLayoutAttributes *)layoutAttributesForDecorationViewOfKind:(NSString *)decorationViewKind atIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes *layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:decorationViewKind withIndexPath:indexPath];
    CGFloat decorationOffset = (indexPath.row + 1) * self.itemSize.height + indexPath.row * self.minimumLineSpacing;
    layoutAttributes.frame = CGRectMake(0.0, decorationOffset, self.collectionViewContentSize.width, self.minimumLineSpacing);
    layoutAttributes.zIndex = 1000;

    return layoutAttributes;
}
Run Code Online (Sandbox Code Playgroud)

--6--

有时我发现这个解决方案给出了装饰外观的视觉故障,这是通过实施来修复的initialLayoutAttributesForAppearingDecorationElementOfKind.

- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingDecorationElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)decorationIndexPath {
    UICollectionViewLayoutAttributes *layoutAttributes =  [self layoutAttributesForDecorationViewOfKind:elementKind atIndexPath:decorationIndexPath];
    return layoutAttributes;
}
Run Code Online (Sandbox Code Playgroud)

就这样.没有太多的代码,但做得对.


Sla*_*hyn 15

Swift中的快速解决方案

1.创建CustomFlowLayout.swift文件并粘贴下一个代码

import UIKit

private let separatorDecorationView = "separator"

final class CustomFlowLayout: UICollectionViewFlowLayout {

    override func awakeFromNib() {
        super.awakeFromNib()
        register(SeparatorView.self, forDecorationViewOfKind: separatorDecorationView)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect) ?? []
        let lineWidth = self.minimumLineSpacing

        var decorationAttributes: [UICollectionViewLayoutAttributes] = []

        // skip first cell
        for layoutAttribute in layoutAttributes where layoutAttribute.indexPath.item > 0 {
            let separatorAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: separatorDecorationView,
                                                                      with: layoutAttribute.indexPath)
            let cellFrame = layoutAttribute.frame
            separatorAttribute.frame = CGRect(x: cellFrame.origin.x,
                                              y: cellFrame.origin.y - lineWidth,
                                              width: cellFrame.size.width,
                                              height: lineWidth)
            separatorAttribute.zIndex = Int.max
            decorationAttributes.append(separatorAttribute)
        }

        return layoutAttributes + decorationAttributes
    }

}

private final class SeparatorView: UICollectionReusableView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = .red
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes) {
        self.frame = layoutAttributes.frame
    }
}
Run Code Online (Sandbox Code Playgroud)

2.设置自定义流程

在界面构建器中,选择您的UICollectionViewFlow并设置我们的新类名 CustomFlowLayout

3.更改分隔符颜色

在SeparatorView中,您可以更改分隔符的颜色 init

4.更改分隔符的高度

你可以用两种不同的方式做到这一点

  • 在故事板上.更改属性Min Spacing for Lines

要么

  • 在代码中.设定值minimumLineSpacing

    override func awakeFromNib() {
        super.awakeFromNib()
        register(SeparatorView.self, forDecorationViewOfKind: separatorDecorationView)
        minimumLineSpacing = 2 }
    
    Run Code Online (Sandbox Code Playgroud)


Wer*_*her 12

Anton的优秀建议,但我认为FlowLayout子类中的实现可以更简单.因为 - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect的超级实现已经返回包括它们的frame和indexPath的单元格的布局属性,所以你有足够的信息来通过仅覆盖此方法并内省单元格布局来计算分隔符的帧.属性:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *layoutAttributesArray = [super layoutAttributesForElementsInRect:rect];

    CGFloat lineWidth = self.minimumLineSpacing;
    NSMutableArray *decorationAttributes = [[NSMutableArray alloc] initWithCapacity:layoutAttributesArray.count];

    for (UICollectionViewLayoutAttributes *layoutAttributes in layoutAttributesArray) {
        //Add separator for every row except the first
        NSIndexPath *indexPath = layoutAttributes.indexPath;
        if (indexPath.item > 0) {
            UICollectionViewLayoutAttributes *separatorAttributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:kCellSeparatorKind withIndexPath:indexPath];
            CGRect cellFrame = layoutAttributes.frame;

            //In my case I have a horizontal grid, where I need vertical separators, but the separator frame can be calculated as needed
            //e.g. top, or both top and left
            separatorAttributes.frame = CGRectMake(cellFrame.origin.x - lineWidth, cellFrame.origin.y, lineWidth, cellFrame.size.height);
            separatorAttributes.zIndex = 1000;
            [decorationAttributes addObject:separatorAttributes];
        }
    }
    return [layoutAttributesArray arrayByAddingObjectsFromArray:decorationAttributes];
}
Run Code Online (Sandbox Code Playgroud)