【问题标题】:Problem with creating Compositional Layout (UICollectionView)创建组合布局(UICollectionView)的问题
【发布时间】:2021-08-03 03:15:37
【问题描述】:

我想使用UIKit 构建以下布局。

目前我将UICollectionViewComposotional Layout 结合使用。以下代码会产生此结果:

相关方法

private static func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
    let headerItem = NSCollectionLayoutItem(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(35)
        )
    )
    
    let horizontalGroupItem = NSCollectionLayoutItem(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.95),
            heightDimension: .fractionalHeight(1.0)
        )
    )
    let horizontalMainGroup = NSCollectionLayoutGroup.horizontal(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(100)
        ),
        subitems: [horizontalGroupItem]
    )
    horizontalMainGroup.interItemSpacing = .fixed(10)
    
    let group = NSCollectionLayoutGroup.vertical(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(135)
        ),
        subitems: [headerItem, horizontalMainGroup])
    
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 10
    section.orthogonalScrollingBehavior = .continuous
    
    let layout = UICollectionViewCompositionalLayout(section: section)
    
    return layout
}

所以主要问题是,顶部元素没有占据部分的整个宽度。相反,它仅限于group 的宽度。有谁知道如何得到想要的结果? :-)

完整示例

import UIKit

class ViewController: UIViewController {
    
    private let colors: [UIColor] = [.systemTeal, .systemBlue, .systemGray, .systemOrange]
    private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: ViewController.createCompositionalLayout())
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        
        collectionView.backgroundColor = .systemBackground
        collectionView.frame = view.bounds
        collectionView.dataSource = self
        collectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: MyCollectionViewCell.reuseID)
    }
    
private static func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
    let headerItem = NSCollectionLayoutItem(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(35)
        )
    )
    
    let horizontalGroupItem = NSCollectionLayoutItem(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.95),
            heightDimension: .fractionalHeight(1.0)
        )
    )
    let horizontalMainGroup = NSCollectionLayoutGroup.horizontal(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(100)
        ),
        subitems: [horizontalGroupItem]
    )
    horizontalMainGroup.interItemSpacing = .fixed(10)
    
    let group = NSCollectionLayoutGroup.vertical(
        layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(135)
        ),
        subitems: [headerItem, horizontalMainGroup])
    
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 10
    section.orthogonalScrollingBehavior = .continuous
    
    let layout = UICollectionViewCompositionalLayout(section: section)
    
    return layout
}

}

extension ViewController: UICollectionViewDataSource {
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 4
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.reuseID, for: indexPath) as? MyCollectionViewCell
        cell?.backgroundColor = colors[indexPath.item]
        cell?.label.text = "\(indexPath.item)"
        
        return cell ?? UICollectionViewCell()
    }
}

//MARK: - CollectionViewCell
class MyCollectionViewCell: UICollectionViewCell {
    static let reuseID = "myCell"
    
    let label = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        contentView.addSubview(label)
        label.frame = contentView.bounds
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

【问题讨论】:

    标签: swift uicollectionview uikit uicollectionviewcompositionallayout


    【解决方案1】:

    目前您将 HeaderItem 添加到组中,这就是它被限制为组的宽度的原因。

    您可以通过 NSCollectionLayoutSectionboundarySupplementaryItems 来实现所需的效果。

    为此,您需要更新您的 HeaderItem

     let headerItem = NSCollectionLayoutBoundarySupplementaryItem(
         layoutSize: NSCollectionLayoutSize(
             widthDimension: .fractionalWidth(1.0),
             heightDimension: .absolute(35)
         ),
         elementKind: "section-header",
         alignment: .top
      )
    

    将其添加到布局部分:

    section.boundarySupplementaryItems = [headerItem]
    

    注册:

    collectionView.register(HeaderRV.self, forSupplementaryViewOfKind: "section-header", withReuseIdentifier: "HeaderRV")
    

    创建 HeaderRV 类:

    class HeaderRV: UICollectionReusableView {
        
        let label = UILabel()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.addSubview(label)
            label.frame = self.bounds
        }
        
        required init?(coder: NSCoder) { 
            fatalError("Not happening") 
        }
    }
    

    最后你需要在你的UICollectionViewDataSource中添加collectionView(_:viewForSupplementaryElementOfKind:at:)

    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderRV", for: indexPath) as! HeaderRV
        
        header.backgroundColor = .red
        header.label.text = "\(indexPath.item)"
        
        return header
    }
    

    完整代码:

    import UIKit
    
    class ViewController: UIViewController {
        
        private let colors: [UIColor] = [.systemTeal, .systemBlue, .systemGray, .systemOrange]
        private let collectionView = UICollectionView(frame: .zero, collectionViewLayout: ViewController.createCompositionalLayout())
        
        override func viewDidLoad() {
            super.viewDidLoad()
            view.addSubview(collectionView)
            
            collectionView.backgroundColor = .systemBackground
            collectionView.frame = CGRect(x: 5, y: 0, width: view.bounds.width - 10, height: view.bounds.height)
            collectionView.dataSource = self
            
            collectionView.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: MyCollectionViewCell.reuseID)
            collectionView.register(HeaderRV.self, forSupplementaryViewOfKind: "section-header", withReuseIdentifier: "HeaderRV")
        }
        
        private static func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
            let headerItem = NSCollectionLayoutBoundarySupplementaryItem(
                layoutSize: NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1.0),
                    heightDimension: .absolute(50)
                ),
                elementKind: "section-header",
                alignment: .top
            )
            headerItem.contentInsets.bottom = 5
                    
            let horizontalGroupItem = NSCollectionLayoutItem(
                layoutSize: NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(1.0),
                    heightDimension: .fractionalHeight(1.0)
                )
            )
                    
            let group = NSCollectionLayoutGroup.horizontal(
                layoutSize: NSCollectionLayoutSize(
                    widthDimension: .fractionalWidth(0.65),
                    heightDimension: .absolute(135)
                ),
                subitems: [horizontalGroupItem])
                    
            let section = NSCollectionLayoutSection(group: group)
            section.interGroupSpacing = 50
            section.boundarySupplementaryItems = [headerItem]
            section.orthogonalScrollingBehavior = .continuous
            
            let layout = UICollectionViewCompositionalLayout(section: section)
            
            return layout
        }
        
    }
    
    extension ViewController: UICollectionViewDataSource {
        func numberOfSections(in collectionView: UICollectionView) -> Int {
            return 1
        }
        
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 4
        }
        
        func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
            let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderRV", for: indexPath) as! HeaderRV
            
            header.backgroundColor = .red
            header.label.text = "\(indexPath.item)"
            
            return header
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.reuseID, for: indexPath) as? MyCollectionViewCell
            cell?.backgroundColor = colors[indexPath.item]
            cell?.label.text = "\(indexPath.item)"
            
            return cell ?? UICollectionViewCell()
        }
    }
    
    //MARK: - CollectionViewCell
    class MyCollectionViewCell: UICollectionViewCell {
        static let reuseID = "myCell"
        
        let label = UILabel()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            contentView.addSubview(label)
            label.frame = contentView.bounds
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    //MARK: - CollectionReusableView
    class HeaderRV: UICollectionReusableView {
        
        let label = UILabel()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.addSubview(label)
            label.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height - 5)
        }
        
        required init?(coder: NSCoder) { fatalError("Not happening") }
    }
    

    【讨论】:

    • 感谢您的回答!它接近预期的结果:-)。然而,这并不是我想要的(见我问题中的第一个 gif);-)…
    • @finebel 我已经更新了答案。我更改了 collectionView.frame 以添加缩进并更改了 createCompositionalLayout() - 现在标题有一个缩进,您当时可以在该部分中看到 2 个项目(您可以通过减少组的 widthDimension 来显示更多项目
    • 这似乎是合理的。然而,这种方法的问题是,如果我们滚动到末尾或开头,我们正在使用一个固定的标题项,它与组的第一项或最后一项不正确对齐。相反,它会在下面的项目根据拖动手势移动时保持其位置。
    猜你喜欢
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    • 2020-10-24
    • 1970-01-01
    • 2020-09-10
    • 2013-04-12
    相关资源
    最近更新 更多