Swift 以编程方式创建自定义UICollectionFlowLayout

Swift 以编程方式创建自定义UICollectionFlowLayout,swift,swift3,uicollectionview,uicollectionviewlayout,uicollectionviewdelegate,Swift,Swift3,Uicollectionview,Uicollectionviewlayout,Uicollectionviewdelegate,我试图创建一个UICollectionView,其中包含一个自定义的FlowLayout,所有这些都是通过编程实现的 我按照本教程介绍了如何通过故事板设置自定义FlowLayout: 但是,我不想使用故事板,而是希望通过代码中的纯逻辑实现它 目前,我在主VC中对collectionView的设置如下: /** Collection View **/ let flowLayout : UICollectionViewFlowLayout = UICollectionViewFlow

我试图创建一个UICollectionView,其中包含一个自定义的FlowLayout,所有这些都是通过编程实现的

我按照本教程介绍了如何通过故事板设置自定义FlowLayout:

但是,我不想使用故事板,而是希望通过代码中的纯逻辑实现它

目前,我在主VC中对collectionView的设置如下:

/** Collection View **/
        let flowLayout : UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        flowLayout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        flowLayout.scrollDirection = .vertical
        self.collectionView = UICollectionView(frame: CGRect(x: self.frame.width * 0, y: self.frame.height * 0, width: self.frame.width, height: self.frame.height), collectionViewLayout: flowLayout)
        collectionView.register(DiscoverCVC.self, forCellWithReuseIdentifier: cellId)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.backgroundColor = UIColor.clear
        self.addSubview(collectionView)
我需要以编程方式注册的内容如下所示

布局需要是“自定义”的,类需要是一个特殊的类

下面是课堂:

import UIKit

private let kReducedHeightColumnIndex = 1
private let kItemHeightAspect: CGFloat = 2

class DiscoverCollection: BaseCollectionViewLayout {

    private var _itemSize : CGSize!
    private var _columnsXoffset : [CGFloat]!

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.totalColumns = 3
    }

    private func isLastItemSingleInRow(_ indexPath: IndexPath) -> Bool {
        return indexPath.item == (totalItemsInSection - 1) && indexPath.item % totalColumns == 0
    }

    override func calculateItemSize() {
        let contentWidthWithoutIndents = collectionView!.bounds.width - contentInsets.left - contentInsets.right
        let itemWidth = (contentWidthWithoutIndents - (CGFloat(totalColumns) - 1) * interItemsSpacing) / CGFloat(totalColumns)

        let itemHeight = itemWidth * kItemHeightAspect
        _itemSize = CGSize(width: itemWidth, height: itemWidth)

        _columnsXoffset = []

        for columnIndex in 0...(totalColumns - 1) {
            _columnsXoffset.append(CGFloat(columnIndex) * (_itemSize.width + interItemsSpacing))
        }
    }

    override func columnIndexForItemAt(indexPath: IndexPath) -> Int {
        let columnIndex = indexPath.item % totalColumns
        return self.isLastItemSingleInRow(indexPath) ? kReducedHeightColumnIndex : columnIndex
    }

    override func calculateItemFrame(indexPath: IndexPath, columnIndex: Int, columnYoffset: CGFloat) -> CGRect {
        let rowIndex = indexPath.item / totalColumns
        let halfItemHeight = (_itemSize.height - interItemsSpacing) / 2

        var itemHeight = _itemSize.height

        if (rowIndex == 0 && columnIndex == kReducedHeightColumnIndex) || self.isLastItemSingleInRow(indexPath) {
            itemHeight = halfItemHeight
        }
        return CGRect(x: _columnsXoffset[columnIndex], y: columnYoffset, width: _itemSize.width, height: itemHeight)
    }
}
以下是基本集合ViewFlowLayout

import UIKit

class BaseCollectionViewLayout: UICollectionViewLayout {

    private var _layoutMap = [IndexPath : UICollectionViewLayoutAttributes]()
    private var _columnsYoffset : [CGFloat]!
    private var _contentSize : CGSize!

    private(set) var totalItemsInSection = 0

    var totalColumns = 0
    var interItemsSpacing : CGFloat = 8

    var contentInsets : UIEdgeInsets {
        return collectionView!.contentInset
    }

    override var collectionViewContentSize: CGSize {
        return _contentSize
    }

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

        for(_, layoutAttributes) in _layoutMap {
            if rect.intersects(layoutAttributes.frame) {
                layoutAttributesArray.append(layoutAttributes)
            }
        }

        return layoutAttributesArray
    }

    func columnIndexForItemAt(indexPath: IndexPath) -> Int {
        return indexPath.item % totalColumns
    }

    func calculateItemFrame(indexPath: IndexPath, columnIndex: Int, columnYoffset: CGFloat) -> CGRect {
        return CGRect.zero
    }

    func calculateItemSize(){}

    override func prepare() {
        _layoutMap.removeAll()
        _columnsYoffset = Array(repeating: 0, count: totalColumns)

        totalItemsInSection = collectionView!.numberOfItems(inSection: 0)

        if totalItemsInSection > 0 && totalColumns > 0 {
            self.calculateItemSize()
            var itemIndex = 0
            var contentSizeHeight : CGFloat = 0

            while itemIndex < totalItemsInSection {

                let indexPath = IndexPath(item: itemIndex, section: 0)
                let columnIndex = self.columnIndexForItemAt(indexPath: indexPath)

                let attributeRect = calculateItemFrame(indexPath: indexPath, columnIndex: columnIndex, columnYoffset: _columnsYoffset[columnIndex])
                let targetLayoutAttributes = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
                targetLayoutAttributes.frame = attributeRect

                contentSizeHeight = max(attributeRect.maxY, contentSizeHeight)
                _columnsYoffset[columnIndex] = attributeRect.maxY + interItemsSpacing
                _layoutMap[indexPath] = targetLayoutAttributes

                itemIndex += 1
            }

            _contentSize = CGSize(width: collectionView!.bounds.width - contentInsets.left - contentInsets.right, height: contentSizeHeight)
        }
    }
}
导入UIKit
类BaseCollectionViewLayout:UICollectionViewLayout{
私有变量\u layoutMap=[IndexPath:UICollectionViewLayoutAttribute]()
私有变量_columnsYoffset:[CGFloat]!
私有var\u contentSize:CGSize!
私有(集合)变量totalItemsInSection=0
var totalColumns=0
var interItemsSpacing:CGFloat=8
var内容插入集:UIEdgeInsets{
返回collectionView!.contentInset
}
覆盖变量collectionViewContentSize:CGSize{
返回内容大小
}
是否覆盖func layoutAttributesForElements(在rect:CGRect中)->[UICollectionViewLayoutAttributes]{
变量layoutAttributesArray=[UICollectionViewLayoutAttributes]()
对于_layoutMap中的(_,layouttributes){
如果矩形相交(layoutAttributes.frame){
layoutAttributesArray.append(layoutAttributes)
}
}
返回布局属性阵列
}
func columnIndexForItemAt(indexPath:indexPath)->Int{
返回indexPath.item%totalColumns
}
func calculateItemFrame(indexath:indexath,columnIndex:Int,columnYoffset:CGFloat)->CGRect{
返回CGRect.zero
}
func calculateItemSize(){}
重写func prepare(){
_layoutMap.removeAll()
_columnsYoffset=数组(重复:0,计数:totalColumns)
totalItemsInSection=collectionView!.numberOfItems(第0节)
如果totalItemsInSection>0&&totalColumns>0{
self.calculateItemSize()
var itemIndex=0
var contentSizeHeight:CGFloat=0
而itemIndex

如何执行此操作?

将此附加方法添加到
DiscoveryCollection

override init() {
    super.init()
    self.totalColumns = 3
}
然后将CollectionView的布局设置为布局实例:

let customLayout = DiscoverCollection()
collectionView.collectionViewLayout = customLayout

将此附加方法添加到
DiscoveryCollection

override init() {
    super.init()
    self.totalColumns = 3
}
然后将CollectionView的布局设置为布局实例:

let customLayout = DiscoverCollection()
collectionView.collectionViewLayout = customLayout

我原以为是这样的,但它告诉我,我缺少参数“coder”,我已经用我尝试使用的类更新了OP,我对flowlayout真的不太了解,抱歉,如果它看起来像一个简单的答案OK,这与集合视图或布局无关。问题在于您的类(及其基类)提供的各种
init
方法,而现在,这只是
init(coder:)
。因为我们不知道BaseCollectionViewLayout是什么样子,所以很难说您需要更改什么。我知道,我已经用BaseCollectionViewLayout更新了OP。更新了我的回答我认为是这样的,但它告诉我我缺少参数“coder”,我已经用我尝试使用的类更新了OP,我对flowlayout还不太了解,真的很抱歉,如果它看起来像一个简单的答案OK,这与集合视图或布局无关。问题在于您的类(及其基类)提供的各种
init
方法,而现在,这只是
init(coder:)
。由于我们不知道
BaseCollectionViewLayout
是什么样子,很难说您需要更改什么。我明白了,我已经用BaseCollectionViewLayout更新了OP。更新了我的回答是否也共享BaseCollectionViewLayout类?添加了它!感谢JaredMind分享BaseCollectionViewLayout类?添加了它!谢谢你,杰瑞德