UICollectionViewCompositionalLayout

UICollectionViewCompositionalLayout

参考

在iOS6中引入的UICollectionView,是一个基于行的布局系统(line-based layout system)。但是现在由于设备的异构型的增强和屏幕大小的改变,情况越来越复杂

所以推出了Compositional Layout 组合布局,其特点

  • Composable - 可组合的,使用简单的东西来制作复杂的东西
  • Flexible - 可以用组合布局来写任何的布局
  • Fast

Compositional Layout是一种描述性API,组合布局使用小的布局组合成更大的布局

Compositional Layout组合布局,不需要使用子类来继承,只需要创建一些东西,将它们组合起来

例如,创建一个简单的list

// Create a List by Specifying Three Core Components: Item, Group and Section
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44.0))

let item = NSCollectionLayoutItem(layoutSize: size)

let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])

let section = NSCollectionLayoutSection(group: group)

let layout = UICollectionViewCompositionalLayout(section: section) 

item被加入到group中,group被加入到section中

结构

Core Concepts

NSCollectionLayoutSize

NSCollectionLayoutSize表示的是大小,这里的size指的是Width和Height的尺寸(dimension)

class NSCollectionLayoutSize {
 init(widthDimension: NSCollectionLayoutDimension, heightDimension: NSCollectionLayoutDimension)
}

注意这里它们不是标量,而是NSCollectionLayoutDimension类型,它是一种不依赖于轴来描述一个具体的轴的大小,有4个方法来定义:

class NSCollectionLayoutDimension {
 class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self
 class func fractionalHeight(_ fractionalHeight: CGFloat) -> Self
 class func absolute(_ absoluteDimension: CGFloat) -> Self
 class func estimated(_ estimatedDimension: CGFloat) -> Self
}

使用fractional variants来创建dimension

let widthDimension = NSCollectionLayoutDimension.fractionalWidth(0.5)
let heightDimension = NSCollectionLayoutDimension.fractionalHeight(0.3)

表示这里的item的宽度将会是其容器宽度的0.5,高度是其容器高度的0.3

let size = NSCollectionLayoutDimension(widthDimension: .fractionalWidth(0.25),
 heightDimension: .fractionalWidth(0.25))

分数

使用point-based值来创建

let heightDimension = NSCollectionLayoutDimension.absolute(200)

表示将元素的宽或者高写死一个绝对的值

预估值

let heightDimension = NSCollectionLayoutDimension.estimated(200)

表示预估高度

NSCollectionLayoutItem

NSCollectionLayoutItem表示的是item的布局,item指的就是cell或者supplementary,即呈现在屏幕上的东西

class NSCollectionLayoutItem {
 convenience init(layoutSize: NSCollectionLayoutSize)
 var contentInsets: NSDirectionalEdgeInsets
}

NSCollectionLayoutGroup

class NSCollectionLayoutGroup: NSCollectionLayoutItem { 
    class func horizontal(layoutSize: NSCollectionLayoutSize, subitems: [NSCollectionLayoutItem]) -> Self 
    class func vertical(layoutSize: NSCollectionLayoutSize, subitems: [NSCollectionLayoutItem]) -> Self 
    class func custom(layoutSize: NSCollectionLayoutSize, itemProvider: NSCollectionLayoutGroupCustomItemProvider) -> Self
}

Group是组成布局的基本单元,它有三种形式

  • 水平
  • 垂直
  • 自定义

如果你没有一个沿着一条line的布局,可使用自定义group。自定义group让你以一种自定义的方式,描述item的绝对大小和位置,如果你有预先定义好的生成器来生成布局,就可以使用自定义group。例如定义一个径向(radial)的布局

而在自定义中需要传入一个 NSCollectionLayoutGroupCustomItemProvider 来决定这个 group 中 Item 得布局方式。通过 NSCollectionLayoutGroup 我们可以实现在同一个 section 中实现不同得布局方式

NSCollectionLayoutSection

class NSCollectionLayoutSection {
    convenience init(layoutGroup: NSCollectionLayoutGroup) 
    var contentInsets: NSDirectionalEdgeInsets
}

初始化section需要group,contentInsets 来决定这个Section内凹的大小

UICollectionViewCompositionalLayout

UICollectionViewCompositionalLayout

class UICollectionViewCompositionalLayout: UICollectionViewLayout { 
	init(section: NSCollectionLayoutSection)
	init(sectionProvider: @escaping SectionProvider)
}

可以指定一个闭包,闭包将会回调,并将在每个section的基础上请求这些section的定义

例子

以一个简单的列表为例:
NumberCell.swift继承自UICollectionViewCell,仅仅包含一个label

class NumberCell: UICollectionViewCell {
    //标识符
    static let reuseIndentifier = String(describing: NumberCell.self)
    
    @IBOutlet weak var label: UILabel!
}

ViewController.swift主要逻辑在于配置组合布局和配置数据源

class ViewController: UIViewController {
    
    //表示section,默认遵循Hashable协议
    enum Section {
        case Main
    }
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    var dataSource: UICollectionViewDiffableDataSource<Section, Int>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        collectionView.collectionViewLayout = configureLayout()
        configureDataSource()
    }
    
    //布局
    func configureLayout() -> UICollectionViewCompositionalLayout {
        
        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)
        
        return UICollectionViewCompositionalLayout(section: section)
        
    }
    
    func configureDataSource() {
        
        //如何设置cell
        dataSource = UICollectionViewDiffableDataSource<Section, Int>(collectionView: self.collectionView, cellProvider: { (collectionView, indexPath, number) -> UICollectionViewCell? in
            
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NumberCell.reuseIndentifier, for: indexPath) as? NumberCell
                else {
                fatalError("Cannot create new cell")
            }
            cell.label.text = number.description
            
            
            return cell
            
        })
        
        //设置数据源
        //先添加section
        var initialSnapshot = NSDiffableDataSourceSnapshot<Section, Int>()
        initialSnapshot.appendSections([.Main])
        //添加item
        initialSnapshot.appendItems(Array(1...100), toSection: .Main)
        //应用数据源
        dataSource.apply(initialSnapshot, animatingDifferences: false)
        
    }
    
    
}

效果如下:
效果
组合布局的好处是经过简单的修改就可以实现不同的效果
例如,如果上面的例子想实现2列布局,只需要将itemSizefractionalWidth这是为0.5

    //布局
    func configureLayout() -> UICollectionViewCompositionalLayout {
        
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), 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)
        
        return UICollectionViewCompositionalLayout(section: section)
        
    }

2列布局
NSCollectionLayoutItem有个contentInsets属性,可以设置inset,实现边距

    //布局
    func configureLayout() -> UICollectionViewCompositionalLayout {
        
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        //contentInsets
        item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
        
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        
        return UICollectionViewCompositionalLayout(section: section)
        
    }

inset效果
实现5列布局

    //布局
    func configureLayout() -> UICollectionViewCompositionalLayout {
        
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2), heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        //contentInsets
        item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
        
        
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(0.2))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        
        let section = NSCollectionLayoutSection(group: group)
        
        return UICollectionViewCompositionalLayout(section: section)
        
    }

5列布局

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值