如何使用装饰器视图在 uicollection 视图中实现所需的设计

How To Achieve the desired Design in uicollection view using decorator view

查看以下各项的边框(Border Spacing)

使用 collection 视图 Header 我能够实现以下输出但卡在如何将分隔符放入 uicollection 视图中。行内的单元格数也是动态的。

最后一行不应该是底部分隔符 非常感谢任何帮助..

为了实现以下布局,我只使用 collection 视图和 header 部分 我已经做了下面的输出

所有部分都已折叠

单击了特定部分

每个扩展部分只剩下分隔符部分 我不知道如何使用装饰视图实现相同的效果。

如果列数始终为 2,为什么不向单元格添加三个视图(分隔符),两个在侧面,一个在底部。对于右侧的单元格,隐藏最右边的分隔符,对于左侧的单元格,反之亦然。 隐藏最后一行单元格底部的分隔符。 这有点 hack,但实施起来要简单得多。

自定义 UICollectionviewcell(我想你可能已经这样做了), 现在在自定义 UICollectionviewcell 上放两个 UIView -

  • 单元格右侧的一个 UIView,宽度为 1 或 2 点(根据 必需)和高度等于单元格高度,给出黑色背景 颜色为 UIView.
  • 单元格底部的另一个 UIView,高度为 1 或 2 点(根据需要),这次宽度等于单元格宽度,为 UIView 提供黑色背景色。 并调整空间。 我觉得这个技巧对你有用。

你可以这样做,

  • 创建两种不同类型的装饰视图,一种是垂直线,位于集合视图的中心,另一种是水平线,出现在两个单元格下方
  • 垂直装饰视图只为整个视图创建一次,而水平装饰视图为每行下方出现的一对两个创建。对于最后一行,不要创建装饰视图。
  • 子类UICollectionViewFlowLayout,并覆盖override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 和return适当的装饰视图。

这是我的布局,

这是用于此的代码,

CollectionViewController,

class ViewController: UICollectionViewController {

    let images = ["Apple", "Banana", "Grapes", "Mango", "Orange", "Strawberry"]

     init() {
        let collectionViewLayout = DecoratedFlowLayout()
        collectionViewLayout.register(HorizontalLineDecorationView.self,
                                      forDecorationViewOfKind: HorizontalLineDecorationView.decorationViewKind)
        collectionViewLayout.register(VerticalLineDecorationView.self,
                                      forDecorationViewOfKind: VerticalLineDecorationView.decorationViewKind)
        super.init(collectionViewLayout: collectionViewLayout)
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView?.backgroundColor = UIColor.white
        collectionView?.register(CollectionViewCell.self,
                                 forCellWithReuseIdentifier: CollectionViewCell.CellIdentifier)
    }
}

extension ViewController: UICollectionViewDelegateFlowLayout {

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return images.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CollectionViewCell.CellIdentifier,
                                                      for: indexPath) as! CollectionViewCell
        let name = images[indexPath.item]
        cell.imageView.image = UIImage(named: "\(name).png")
        cell.label.text = name
        return cell
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return .zero
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 10 + DecoratedFlowLayout.horizontalLineWidth
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let width = (collectionView.bounds.size.width - DecoratedFlowLayout.verticalLineWidth)  * 0.5
        return CGSize(width: width,
                      height: width + 20)
    }
}

CollectionViewCell,

class CollectionViewCell: UICollectionViewCell {

    static let CellIdentifier = "CollectionViewCellIdentifier"

    var imageView: UIImageView!
    var label: UILabel!

    override init(frame: CGRect) {
        super.init(frame: frame)
        createViews()
    }

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

    func createViews() {
        imageView = UIImageView(frame: .zero)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(imageView)

        label = UILabel(frame: .zero)
        label.font = UIFont.systemFont(ofSize: 20)
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(label)

        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
            imageView.leftAnchor.constraint(equalTo: contentView.leftAnchor),
            imageView.rightAnchor.constraint(equalTo: contentView.rightAnchor),
            label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            label.leftAnchor.constraint(equalTo: contentView.leftAnchor),
            label.rightAnchor.constraint(equalTo: contentView.rightAnchor),
            label.topAnchor.constraint(equalTo: imageView.bottomAnchor),
            label.heightAnchor.constraint(equalToConstant: 20)
            ])
    }
}

DecoratedFlowLayout,

class DecoratedFlowLayout: UICollectionViewFlowLayout {

    static let verticalLineWidth: CGFloat = 20
    static let horizontalLineWidth: CGFloat = 20

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        minimumLineSpacing = 40 // should be equal to or greater than horizontalLineWidth 
    }

    override init() {
        super.init()
        minimumLineSpacing = 40
   }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

        guard let attributes = super.layoutAttributesForElements(in: rect) else {
            return nil
        }

        var attributesCopy: [UICollectionViewLayoutAttributes] = []

        for attribute in attributes {

            attributesCopy += [attribute]

            let indexPath = attribute.indexPath

            if collectionView!.numberOfItems(inSection: indexPath.section) == 0 {
                continue
            }

            let firstCell = IndexPath(item: 0,
                                      section: indexPath.section)
            let lastCell = IndexPath(item: collectionView!.numberOfItems(inSection: indexPath.section) - 1,
                                     section: indexPath.section)

            if let attributeForFirstItem = layoutAttributesForItem(at: firstCell),
                let attributeForLastItem = layoutAttributesForItem(at: lastCell) {


                let verticalLineDecorationView = UICollectionViewLayoutAttributes(forDecorationViewOfKind: VerticalLineDecorationView.decorationViewKind,
                                                                                  with: IndexPath(item: 0, section: indexPath.section))

                let firstFrame = attributeForFirstItem.frame
                let lastFrame = attributeForLastItem.frame

                let frame = CGRect(x: collectionView!.bounds.midX - DecoratedFlowLayout.verticalLineWidth * 0.5,
                                   y: firstFrame.minY,
                                   width: DecoratedFlowLayout.verticalLineWidth,
                                   height: lastFrame.maxY - firstFrame.minY)
                verticalLineDecorationView.frame =  frame

                attributesCopy += [verticalLineDecorationView]
            }


            let contains = attributesCopy.contains { layoutAttribute in
                layoutAttribute.indexPath == indexPath
                    && layoutAttribute.representedElementKind == HorizontalLineDecorationView.decorationViewKind
            }

            let numberOfItemsInSection = collectionView!.numberOfItems(inSection: indexPath.section)

            if indexPath.item % 2 == 0 && !contains  && indexPath.item < numberOfItemsInSection - 2 {

                let horizontalAttribute = UICollectionViewLayoutAttributes(forDecorationViewOfKind: HorizontalLineDecorationView.decorationViewKind,
                                                                           with: indexPath)

                let frame = CGRect(x: attribute.frame.minX,
                                   y: attribute.frame.maxY + (minimumLineSpacing - DecoratedFlowLayout.horizontalLineWidth) * 0.5,
                                   width: collectionView!.bounds.width,
                                   height: DecoratedFlowLayout.horizontalLineWidth)

                horizontalAttribute.frame = frame

                attributesCopy += [horizontalAttribute]
            }
        }
        return attributesCopy
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
}

及装修浏览量

class VerticalLineDecorationView: UICollectionReusableView {

    static let decorationViewKind = "VerticalLineDecorationView"

    let verticalInset: CGFloat = 40

    let lineWidth: CGFloat = 4.0

    let lineView = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)

        lineView.backgroundColor = .black

        addSubview(lineView)

        lineView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            lineView.widthAnchor.constraint(equalToConstant: lineWidth),
            lineView.topAnchor.constraint(equalTo: topAnchor, constant: verticalInset),
            lineView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -verticalInset),
            lineView.centerXAnchor.constraint(equalTo: centerXAnchor),
            ])
    }

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


class HorizontalLineDecorationView: UICollectionReusableView {

    let horizontalInset: CGFloat = 20

    let lineWidth: CGFloat = 4.0

    static let decorationViewKind = "HorizontalLineDecorationView"

    let lineView = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)

        lineView.backgroundColor = .black

        addSubview(lineView)

        lineView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            lineView.heightAnchor.constraint(equalToConstant: lineWidth),
            lineView.leftAnchor.constraint(equalTo: leftAnchor, constant: horizontalInset),
            lineView.rightAnchor.constraint(equalTo: rightAnchor, constant: -horizontalInset),
            lineView.centerYAnchor.constraint(equalTo: centerYAnchor),
            ])
    }

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

我希望你能利用它并调整值以满足你自己的需要。某些计算可能对某些级别的更改有意义。但是,我希望您了解如何实现这一目标。