在 Compositional Layout 中自定义 UICollectionView 部分的拐角半径和颜色?

Customizing corner radius & colors for UICollectionView sections in Compositional Layout?

提问人:Shivaditya kr 提问时间:10/13/2023 更新时间:10/19/2023 访问量:66

问:

我正在处理一个项目,其中我正在使用带有组合布局的 UICollectionView。我正在尝试将拐角半径添加到我的 UICollectionView 的节标题中。我正在使用 UICollectionViewCompositionalLayout 创建部分,并且我希望每个部分标题具有不同的角半径、颜色和设计。

下面是我的代码示例:

    // Creating the compositional layout
    let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in
        // configuring sections and items
    }

    // Registering section header
    let headerRegistration = UICollectionView.SupplementaryRegistration
        <HeaderCollectionViewCell>(elementKind: UICollectionView.elementKindSectionHeader) {
        supplementaryView, string, indexPath in
        // configuring header view
    }

    collectionView.collectionViewLayout = layout
    collectionView.register(HeaderCollectionViewCell.self, 
        forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 
        withReuseIdentifier: "HeaderCollectionViewCell")

如何使用 UICollectionViewCompositionalLayout 为 UICollectionView 的每个部分标题添加不同的角半径、颜色和设计?有关此问题的任何帮助或指导将不胜感激。谢谢!

我试图通过在部分内创建装饰器来装饰它,但似乎我只能更改颜色而不能更改角半径。此外,我添加的颜色不符合部分约束。例如,如果我向某个部分添加水平填充,则该部分颜色会溢出该填充并扩展到屏幕宽度

Swift UIClecutionView UIKiter FlowLayout 修饰

评论


答:

0赞 DonMag 10/16/2023 #1

您可以在注册块中执行此操作。

例如:

    let headerRegistration = UICollectionView.SupplementaryRegistration
    <TitleSupplementaryView>(elementKind: CompColumnsVC.sectionHeaderElementKind) {
        (supplementaryView, string, indexPath) in
        
        supplementaryView.label.text = "\(string) for section \(indexPath.section)"
        
        // default background color / corner radius /
        //  text color / border color / border width
        supplementaryView.backgroundColor = .lightGray
        supplementaryView.layer.cornerRadius = 0.0
        
        supplementaryView.layer.borderColor = UIColor.black.cgColor
        supplementaryView.layer.borderWidth = 1.0
        supplementaryView.label.textColor = .black
        
        // specific background color / corner radius /
        //  text color / border color / border width
        //  for sections 0, 1, 2 (all the rest use default
        switch indexPath.section {
        case 0:
            supplementaryView.backgroundColor = .cyan
            supplementaryView.layer.cornerRadius = 6.0

        case 1:
            supplementaryView.backgroundColor = .systemBlue
            supplementaryView.label.textColor = .white
            supplementaryView.layer.cornerRadius = 12.0

        case 2:
            supplementaryView.backgroundColor = .yellow
            supplementaryView.layer.cornerRadius = 16.0
            supplementaryView.layer.borderWidth = 0.0
            supplementaryView.layer.borderColor = UIColor.red.cgColor

        default:
            ()
        }
        
    }

给我这个结果:

enter image description here

下面是一个完整的示例,基于

  • 构图布局 ->
  • 高级布局 ->
  • 补充意见 ->
  • 节眉/页脚

来自 Apple 的实现新式集合视图示例应用:From Apple's Implementing Modern Collection Views sample app:


简单的单标签集合视图单元格

class SimpleCell: UICollectionViewCell {
    
    let theLabel: UILabel = {
        let v = UILabel()
        return v
    }()
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        theLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(theLabel)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            theLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
            theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),
        ])
        
        contentView.layer.borderColor = UIColor(white: 0.9, alpha: 1.0).cgColor
        contentView.layer.borderWidth = 1.0
    }
}

节标题的可重用视图

class TitleSupplementaryView: UICollectionReusableView {
    
    let label = UILabel()
    let bkgView = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    func commonInit() {
        
        label.translatesAutoresizingMaskIntoConstraints = false
        bkgView.translatesAutoresizingMaskIntoConstraints = false
        bkgView.addSubview(label)
        addSubview(bkgView)
        
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: bkgView.topAnchor, constant: 8.0),
            label.leadingAnchor.constraint(equalTo: bkgView.leadingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: bkgView.trailingAnchor, constant: -8.0),
            label.bottomAnchor.constraint(equalTo: bkgView.bottomAnchor, constant: -8.0),
            
            bkgView.topAnchor.constraint(equalTo: topAnchor, constant: 4.0),
            bkgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            bkgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            bkgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4.0),
        ])

        label.font = .systemFont(ofSize: 14.0, weight: .light)

        bkgView.backgroundColor = .clear

    }
}

视图控制器类示例

class CustomizeHeadersVC: UIViewController, UICollectionViewDelegate {
    
    var collectionView: UICollectionView!
    
    var dataSource: UICollectionViewDiffableDataSource<Int, Int>! = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout())
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
        ])
        
        configureDataSource()

        collectionView.delegate = self

    }
    
    static let sectionHeaderElementKind = "section-header-element-kind"

    func createLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .absolute(44))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = 3
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 6, trailing: 0)
        
        let headerFooterSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                      heightDimension: .estimated(44))
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: headerFooterSize,
            elementKind: CustomizeHeadersVC.sectionHeaderElementKind, alignment: .top)
        
        section.boundarySupplementaryItems = [sectionHeader]
        
        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }

    func configureDataSource() {
        
        let cellRegistration = UICollectionView.CellRegistration<SimpleCell, Int> { (cell, indexPath, identifier) in
            // Populate the cell with our item description.
            cell.theLabel.text = "\(indexPath)" // "\(indexPath.section),\(indexPath.item)"
        }
        
        let headerRegistration = UICollectionView.SupplementaryRegistration
        <TitleSupplementaryView>(elementKind: CustomizeHeadersVC.sectionHeaderElementKind) {
            (supplementaryView, string, indexPath) in
            
            supplementaryView.label.text = "Section Header for section \(indexPath.section)"
            
            // default background color / corner radius /
            //  text color / border color / border width
            supplementaryView.bkgView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
            supplementaryView.bkgView.layer.cornerRadius = 0.0
            
            supplementaryView.bkgView.layer.borderColor = UIColor.black.cgColor
            supplementaryView.bkgView.layer.borderWidth = 1.0
            supplementaryView.label.textColor = .black
            
            // specific background color / corner radius /
            //  text color / border color / border width
            //  for sections ... cycle through 4 "styles"
            switch indexPath.section % 4 {
            case 0:
                supplementaryView.bkgView.backgroundColor = .cyan
                supplementaryView.bkgView.layer.cornerRadius = 6.0
                
            case 1:
                supplementaryView.bkgView.backgroundColor = .systemBlue
                supplementaryView.label.textColor = .white
                supplementaryView.bkgView.layer.cornerRadius = 12.0
                supplementaryView.bkgView.layer.borderWidth = 2.0
                
            case 2:
                supplementaryView.bkgView.backgroundColor = .yellow
                supplementaryView.bkgView.layer.cornerRadius = 16.0
                supplementaryView.bkgView.layer.borderWidth = 0.0
                supplementaryView.bkgView.layer.borderColor = UIColor.red.cgColor
                
            default:
                ()
            }
            
        }
        
        dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }
        
        dataSource.supplementaryViewProvider = { (view, kind, index) in
            return self.collectionView.dequeueConfiguredReusableSupplementary(
                using: headerRegistration, for: index)
        }

        // initial data
        let itemsPerSection = 3
        let sections = Array(0..<25)
        var snapshot = NSDiffableDataSourceSnapshot<Int, Int>()
        var itemOffset = 0
        sections.forEach {
            snapshot.appendSections([$0])
            snapshot.appendItems(Array(itemOffset..<itemOffset + itemsPerSection))
            itemOffset += itemsPerSection
        }
        dataSource.apply(snapshot, animatingDifferences: false)
    }

}

输出 - 我们循环浏览 4 种不同的部分标题“样式”(背景和文本颜色、边框、角半径等):

enter image description here


编辑 - 评论后...

对上述代码稍作修改,以显示“部分背景”装饰视图......

剖面背景视图

class SectionBackgroundView: UICollectionReusableView {
    
    static let reuseIdentifier: String = "SectionBackgroundView"
    
    let bkgView = UIView()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    func commonInit() {
        backgroundColor = .clear
        
        bkgView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(bkgView)
        
        NSLayoutConstraint.activate([
            bkgView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
            bkgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
            bkgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
            bkgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
        ])
        
    }
    
}

视图控制器类示例

class CustomizeHeadersVC: UIViewController, UICollectionViewDelegate {
    
    var collectionView: UICollectionView!
    
    var dataSource: UICollectionViewDiffableDataSource<Int, Int>! = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: createLayout())
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
        ])
        
        configureDataSource()

        collectionView.delegate = self

    }
    
    func collectionView(_ collectionView: UICollectionView, willDisplaySupplementaryView view: UICollectionReusableView, forElementKind elementKind: String, at indexPath: IndexPath) {

        if elementKind == CustomizeHeadersVC.backgroundElementKind,
           let v = view as? SectionBackgroundView
        {

            // default background color / corner radius /
            //  text color / border color / border width
            v.bkgView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
            v.bkgView.layer.cornerRadius = 0.0
            
            v.bkgView.layer.borderColor = UIColor.black.cgColor
            v.bkgView.layer.borderWidth = 1.0
            
            // specific background color / corner radius /
            //  text color / border color / border width
            //  for sections ... cycle through 4 "styles"
            switch indexPath.section % 4 {
            case 0:
                v.bkgView.backgroundColor = .cyan
                v.bkgView.layer.cornerRadius = 6.0
                
            case 1:
                v.bkgView.backgroundColor = .systemBlue
                v.bkgView.layer.cornerRadius = 12.0
                v.bkgView.layer.borderWidth = 2.0
                
            case 2:
                v.bkgView.backgroundColor = .systemYellow
                v.bkgView.layer.cornerRadius = 16.0
                v.bkgView.layer.borderWidth = 0.0
                v.bkgView.layer.borderColor = UIColor.red.cgColor
                
            default:
                ()
            }

            if let bc = v.bkgView.backgroundColor {
                v.bkgView.backgroundColor = bc.withAlphaComponent(0.25)
            }
        }
    }
    
    static let sectionHeaderElementKind = "section-header-element-kind"
    static let backgroundElementKind = "background-element-kind"

    func createLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                               heightDimension: .absolute(44))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = 3
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 6, trailing: 0)
        section.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 20, bottom: 16, trailing: 20)

        let headerFooterSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                      heightDimension: .estimated(44))
        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: headerFooterSize,
            elementKind: CustomizeHeadersVC.sectionHeaderElementKind, alignment: .top)
        
        section.boundarySupplementaryItems = [sectionHeader]
        
        section.decorationItems = [
            NSCollectionLayoutDecorationItem.background(elementKind: CustomizeHeadersVC.backgroundElementKind)
        ]

        let config = UICollectionViewCompositionalLayoutConfiguration()
        config.interSectionSpacing = 12 // section spacing

        let layout = UICollectionViewCompositionalLayout(section: section, configuration: config)

        layout.register(SectionBackgroundView.self, forDecorationViewOfKind: CustomizeHeadersVC.backgroundElementKind)

        return layout
    }

    func configureDataSource() {
        
        let cellRegistration = UICollectionView.CellRegistration<SimpleCell, Int> { (cell, indexPath, identifier) in
            // Populate the cell with our item description.
            cell.theLabel.text = "\(indexPath)" // "\(indexPath.section),\(indexPath.item)"
        }
        
        let bkgRegistration = UICollectionView.SupplementaryRegistration
        <SectionBackgroundView>(elementKind: CustomizeHeadersVC.backgroundElementKind) {
            (supplementaryView, string, indexPath) in
            
            supplementaryView.bkgView.backgroundColor = .green
        }
        
        let headerRegistration = UICollectionView.SupplementaryRegistration
        <TitleSupplementaryView>(elementKind: CustomizeHeadersVC.sectionHeaderElementKind) {
            (supplementaryView, string, indexPath) in
            
            supplementaryView.label.text = "Section Header for section \(indexPath.section)"
            
            // default background color / corner radius /
            //  text color / border color / border width
            supplementaryView.bkgView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
            supplementaryView.bkgView.layer.cornerRadius = 0.0
            
            supplementaryView.bkgView.layer.borderColor = UIColor.black.cgColor
            supplementaryView.bkgView.layer.borderWidth = 1.0
            supplementaryView.label.textColor = .black
            
            // specific background color / corner radius /
            //  text color / border color / border width
            //  for sections ... cycle through 4 "styles"
            switch indexPath.section % 4 {
            case 0:
                supplementaryView.bkgView.backgroundColor = .cyan
                supplementaryView.bkgView.layer.cornerRadius = 6.0
                
            case 1:
                supplementaryView.bkgView.backgroundColor = .systemBlue
                supplementaryView.label.textColor = .white
                supplementaryView.bkgView.layer.cornerRadius = 12.0
                supplementaryView.bkgView.layer.borderWidth = 2.0
                
            case 2:
                supplementaryView.bkgView.backgroundColor = .systemYellow
                supplementaryView.bkgView.layer.cornerRadius = 16.0
                supplementaryView.bkgView.layer.borderWidth = 0.0
                supplementaryView.bkgView.layer.borderColor = UIColor.red.cgColor
                
            default:
                ()
            }
            
        }
        
        dataSource = UICollectionViewDiffableDataSource<Int, Int>(collectionView: collectionView) {
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: Int) -> UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
        }
        
        dataSource.supplementaryViewProvider = { (view, kind, index) in
            return self.collectionView.dequeueConfiguredReusableSupplementary(
                using: headerRegistration, for: index)
        }

        // initial data
        let itemsPerSection = 3
        let sections = Array(0..<25)
        var snapshot = NSDiffableDataSourceSnapshot<Int, Int>()
        var itemOffset = 0
        sections.forEach {
            snapshot.appendSections([$0])
            snapshot.appendItems(Array(itemOffset..<itemOffset + itemsPerSection))
            itemOffset += itemsPerSection
        }
        dataSource.apply(snapshot, animatingDifferences: false)
    }

}

结果:

enter image description here

评论

0赞 Shivaditya kr 10/16/2023
谢谢你的分享,我也想知道如何装饰我的小组,这样我就可以让每个部分元素都有不同的背景风格。
0赞 DonMag 10/16/2023
好吧,这是一个完全不同的问题。
0赞 DonMag 10/17/2023
@Shivadityakr - 快速搜索会得到很多结果。swift "UICollectionViewCompositionalLayout" section border
0赞 DonMag 10/18/2023
@Shivadityakr - 这是否更接近您想要的?i.stack.imgur.com/6StP7.png
0赞 Shivaditya kr 10/19/2023
嘿,我想,谢谢伙计