Ios UICollectionView设置列数

Ios UICollectionView设置列数,ios,objective-c,xcode,uicollectionview,uicollectionviewcell,Ios,Objective C,Xcode,Uicollectionview,Uicollectionviewcell,我刚开始学习UICollectionView。我想知道是否有人知道如何指定collectionview中的列数。默认设置为3(iPhone/纵向)。我看了文档,似乎找不到一个简洁的答案。因为UICollectionView非常灵活,有多种方法可以更改列数,具体取决于您使用的布局类型 UICollectionViewFlowLayout(这可能是您正在使用的)没有直接指定列数(因为它取决于视图大小/方向)。更改它的最简单方法是设置itemSize属性和/或minimumInteritemSpaci

我刚开始学习UICollectionView。我想知道是否有人知道如何指定collectionview中的列数。默认设置为3(iPhone/纵向)。我看了文档,似乎找不到一个简洁的答案。

因为
UICollectionView
非常灵活,有多种方法可以更改列数,具体取决于您使用的布局类型


UICollectionViewFlowLayout
(这可能是您正在使用的)没有直接指定列数(因为它取决于视图大小/方向)。更改它的最简单方法是设置
itemSize
属性和/或
minimumInteritemSpacing
/
minimumLineSpacing

CollectionView功能强大,而且价格低廉。很多,很多选择。正如欧姆兹所说:

有多种方法可以更改列数

let numberOfColumns: CGFloat = 3
我建议实施该协议,让您可以访问以下方法,在这些方法中,您可以更好地控制
UICollectionView
的布局,而无需对其进行子类化:

  • collectionView:layout:insetforsessionatindex:
  • collectionView:layout:minimuminetemspacingforsectionindex:
  • collectionView:layout:minimumlinespacingforsection索引:
  • collectionView:layout:referenceSizeForFooterInSection:
  • collectionView:layout:referenceSizeForHeaderInstruction:
  • collectionView:layout:sizeFormiteIndeXPath:
此外,实现以下方法将强制UICollectionView在方向更改时更新其布局:(假设您希望为横向调整单元格大小并使其拉伸)

此外,这里有两个关于
UICollectionViews
的非常好的教程:


这一切都是关于你想要绘制的布局。您可以创建从UICollectionViewFlowLayout继承的自定义类。目前没有任何直接方法来设置列。如果你想实现这种功能,你需要手动完成。您需要在自定义流布局类中处理它

现在问题来了,你们怎么做?如果你不想干扰细胞框架,你可以调整

 collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
 collectionView:layout:minimumLineSpacingForSectionAtIndex:
另一种方法是提供您自己的单元格位置。通过重写以下两个方法,在布局形成期间将调用这两个方法

  - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect
  - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path

UICollectionViewLayoutAttribute是一个类,它将处理单元格位置、帧、Zindex等

我在我的
UICollectionViewController
上实现的
UICollectionViewDelegateFlowLayout
,并覆盖负责确定单元格大小的方法。然后,我计算屏幕宽度,并将其与我的列要求分开。例如,我希望每个屏幕大小有3列。我的代码是这样的-

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    CGFloat screenWidth = screenRect.size.width;
    float cellWidth = screenWidth / 3.0; //Replace the divisor with the column count requirement. Make sure to have it in float.
    CGSize size = CGSizeMake(cellWidth, cellWidth);

    return size;
}

更新为Swift 3:

class CollectionViewCell: UICollectionViewCell {

@IBOutlet weak var label: UILabel!

override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
    self.backgroundColor = UIColor.black
    label.textColor = UIColor.white
}
}

class CollectionViewHeadCell: UICollectionViewCell {

@IBOutlet weak var label: UILabel!

override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
    self.backgroundColor = UIColor.darkGray
    label.textColor = UIColor.white
}
}
let cellHeight: CGFloat = 100
let cellWidth: CGFloat = 100

class CustomCollectionViewLayout: UICollectionViewLayout {
    private var numberOfColumns: Int!
    private var numberOfRows: Int!

    // It is two dimension array of itemAttributes
    private var itemAttributes = [[UICollectionViewLayoutAttributes]]()
    // It is one dimension of itemAttributes
    private var cache = [UICollectionViewLayoutAttributes]()

override func prepare() {
    if self.cache.isEmpty {

        self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0)
        self.numberOfRows = self.collectionView?.numberOfSections

        // Dynamically change cellWidth if total cell width is smaller than whole bounds
        /* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth {
         self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns)
         }
         */
        for row in 0..<self.numberOfRows {
            var row_temp = [UICollectionViewLayoutAttributes]()
            for column in 0..<self.numberOfColumns {

                let indexPath = NSIndexPath(item: column, section: row)

                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
                attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight)

                row_temp.append(attributes)

                self.cache.append(attributes)
            }
            self.itemAttributes.append(row_temp)
        }
    }
}
override var collectionViewContentSize: CGSize {
    return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight)
}

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

    var layoutAttributes = [UICollectionViewLayoutAttributes]()

    for attributes in cache {
        if attributes.frame.intersects(rect) {
            layoutAttributes.append(attributes)
        }
    }
    return layoutAttributes
}
}
let CellIdentifier = "CellIdentifier"
let HeadCellIdentifier = "HeadCellIdentifier"

class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {

init() {
    let layout = CustomCollectionViewLayout()

    super.init(frame: CGRect.zero, collectionViewLayout: layout)

    self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier)
    self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier)

    self.isDirectionalLockEnabled = true
    self.dataSource = self
    self.delegate = self
}

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

func updateCollectionView() {
    DispatchQueue.main.async {
        self.reloadData()
    }
}

// MARK: CollectionView datasource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 20
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 20
}
override func numberOfItems(inSection section: Int) -> Int {
    return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    let column = (indexPath as NSIndexPath).row
    let row = (indexPath as NSIndexPath).section

    if column == 0 {
        let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell

        cell.label.text = "\(row)"

        return cell
    }
    else if row == 0 {
        let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell

        cell.label.text = "\(column)"

        return cell
    }
    else {
        let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell

        cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row])

        return cell
    }
}

// MARK: CollectionView delegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    let column = (indexPath as NSIndexPath).row
    let row = (indexPath as NSIndexPath).section

    print("\(column)  \(row)")
}
}
class ViewController: UIViewController {
let collectionView = CollectionView()

override func viewDidLoad() {
    collectionView.translatesAutoresizingMaskIntoConstraints = false
    self.view.addSubview(collectionView)
    self.view.backgroundColor = UIColor.red

    self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
    self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    collectionView.updateCollectionView()
}
}
我更喜欢对特定的列号和行号使用自定义布局,而不是流布局。因为:

  • 如果列数很大,可以水平拖动
  • 由于使用了列和行,因此在逻辑上更易于接受
  • 普通单元格和标题单元格:(将UILabel作为IBOutlet添加到您的xib):

    class CollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.black
        label.textColor = UIColor.white
    }
    }
    
    class CollectionViewHeadCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.darkGray
        label.textColor = UIColor.white
    }
    }
    
    let cellHeight: CGFloat = 100
    let cellWidth: CGFloat = 100
    
    class CustomCollectionViewLayout: UICollectionViewLayout {
        private var numberOfColumns: Int!
        private var numberOfRows: Int!
    
        // It is two dimension array of itemAttributes
        private var itemAttributes = [[UICollectionViewLayoutAttributes]]()
        // It is one dimension of itemAttributes
        private var cache = [UICollectionViewLayoutAttributes]()
    
    override func prepare() {
        if self.cache.isEmpty {
    
            self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0)
            self.numberOfRows = self.collectionView?.numberOfSections
    
            // Dynamically change cellWidth if total cell width is smaller than whole bounds
            /* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth {
             self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns)
             }
             */
            for row in 0..<self.numberOfRows {
                var row_temp = [UICollectionViewLayoutAttributes]()
                for column in 0..<self.numberOfColumns {
    
                    let indexPath = NSIndexPath(item: column, section: row)
    
                    let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
                    attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight)
    
                    row_temp.append(attributes)
    
                    self.cache.append(attributes)
                }
                self.itemAttributes.append(row_temp)
            }
        }
    }
    override var collectionViewContentSize: CGSize {
        return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight)
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
    
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                layoutAttributes.append(attributes)
            }
        }
        return layoutAttributes
    }
    }
    
    let CellIdentifier = "CellIdentifier"
    let HeadCellIdentifier = "HeadCellIdentifier"
    
    class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
    
    init() {
        let layout = CustomCollectionViewLayout()
    
        super.init(frame: CGRect.zero, collectionViewLayout: layout)
    
        self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier)
        self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier)
    
        self.isDirectionalLockEnabled = true
        self.dataSource = self
        self.delegate = self
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func updateCollectionView() {
        DispatchQueue.main.async {
            self.reloadData()
        }
    }
    
    // MARK: CollectionView datasource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 20
    }
    override func numberOfItems(inSection section: Int) -> Int {
        return 20
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        if column == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(row)"
    
            return cell
        }
        else if row == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(column)"
    
            return cell
        }
        else {
            let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell
    
            cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row])
    
            return cell
        }
    }
    
    // MARK: CollectionView delegate
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        print("\(column)  \(row)")
    }
    }
    
    class ViewController: UIViewController {
    let collectionView = CollectionView()
    
    override func viewDidLoad() {
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(collectionView)
        self.view.backgroundColor = UIColor.red
    
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        collectionView.updateCollectionView()
    }
    }
    
    自定义布局:

    class CollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.black
        label.textColor = UIColor.white
    }
    }
    
    class CollectionViewHeadCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.darkGray
        label.textColor = UIColor.white
    }
    }
    
    let cellHeight: CGFloat = 100
    let cellWidth: CGFloat = 100
    
    class CustomCollectionViewLayout: UICollectionViewLayout {
        private var numberOfColumns: Int!
        private var numberOfRows: Int!
    
        // It is two dimension array of itemAttributes
        private var itemAttributes = [[UICollectionViewLayoutAttributes]]()
        // It is one dimension of itemAttributes
        private var cache = [UICollectionViewLayoutAttributes]()
    
    override func prepare() {
        if self.cache.isEmpty {
    
            self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0)
            self.numberOfRows = self.collectionView?.numberOfSections
    
            // Dynamically change cellWidth if total cell width is smaller than whole bounds
            /* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth {
             self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns)
             }
             */
            for row in 0..<self.numberOfRows {
                var row_temp = [UICollectionViewLayoutAttributes]()
                for column in 0..<self.numberOfColumns {
    
                    let indexPath = NSIndexPath(item: column, section: row)
    
                    let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
                    attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight)
    
                    row_temp.append(attributes)
    
                    self.cache.append(attributes)
                }
                self.itemAttributes.append(row_temp)
            }
        }
    }
    override var collectionViewContentSize: CGSize {
        return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight)
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
    
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                layoutAttributes.append(attributes)
            }
        }
        return layoutAttributes
    }
    }
    
    let CellIdentifier = "CellIdentifier"
    let HeadCellIdentifier = "HeadCellIdentifier"
    
    class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
    
    init() {
        let layout = CustomCollectionViewLayout()
    
        super.init(frame: CGRect.zero, collectionViewLayout: layout)
    
        self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier)
        self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier)
    
        self.isDirectionalLockEnabled = true
        self.dataSource = self
        self.delegate = self
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func updateCollectionView() {
        DispatchQueue.main.async {
            self.reloadData()
        }
    }
    
    // MARK: CollectionView datasource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 20
    }
    override func numberOfItems(inSection section: Int) -> Int {
        return 20
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        if column == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(row)"
    
            return cell
        }
        else if row == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(column)"
    
            return cell
        }
        else {
            let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell
    
            cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row])
    
            return cell
        }
    }
    
    // MARK: CollectionView delegate
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        print("\(column)  \(row)")
    }
    }
    
    class ViewController: UIViewController {
    let collectionView = CollectionView()
    
    override func viewDidLoad() {
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(collectionView)
        self.view.backgroundColor = UIColor.red
    
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        collectionView.updateCollectionView()
    }
    }
    
    使用ViewController中的CollectionView:

    class CollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.black
        label.textColor = UIColor.white
    }
    }
    
    class CollectionViewHeadCell: UICollectionViewCell {
    
    @IBOutlet weak var label: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        self.backgroundColor = UIColor.darkGray
        label.textColor = UIColor.white
    }
    }
    
    let cellHeight: CGFloat = 100
    let cellWidth: CGFloat = 100
    
    class CustomCollectionViewLayout: UICollectionViewLayout {
        private var numberOfColumns: Int!
        private var numberOfRows: Int!
    
        // It is two dimension array of itemAttributes
        private var itemAttributes = [[UICollectionViewLayoutAttributes]]()
        // It is one dimension of itemAttributes
        private var cache = [UICollectionViewLayoutAttributes]()
    
    override func prepare() {
        if self.cache.isEmpty {
    
            self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0)
            self.numberOfRows = self.collectionView?.numberOfSections
    
            // Dynamically change cellWidth if total cell width is smaller than whole bounds
            /* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth {
             self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns)
             }
             */
            for row in 0..<self.numberOfRows {
                var row_temp = [UICollectionViewLayoutAttributes]()
                for column in 0..<self.numberOfColumns {
    
                    let indexPath = NSIndexPath(item: column, section: row)
    
                    let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
                    attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight)
    
                    row_temp.append(attributes)
    
                    self.cache.append(attributes)
                }
                self.itemAttributes.append(row_temp)
            }
        }
    }
    override var collectionViewContentSize: CGSize {
        return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight)
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
    
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                layoutAttributes.append(attributes)
            }
        }
        return layoutAttributes
    }
    }
    
    let CellIdentifier = "CellIdentifier"
    let HeadCellIdentifier = "HeadCellIdentifier"
    
    class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource {
    
    init() {
        let layout = CustomCollectionViewLayout()
    
        super.init(frame: CGRect.zero, collectionViewLayout: layout)
    
        self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier)
        self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier)
    
        self.isDirectionalLockEnabled = true
        self.dataSource = self
        self.delegate = self
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func updateCollectionView() {
        DispatchQueue.main.async {
            self.reloadData()
        }
    }
    
    // MARK: CollectionView datasource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 20
    }
    override func numberOfItems(inSection section: Int) -> Int {
        return 20
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        if column == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(row)"
    
            return cell
        }
        else if row == 0 {
            let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell
    
            cell.label.text = "\(column)"
    
            return cell
        }
        else {
            let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell
    
            cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row])
    
            return cell
        }
    }
    
    // MARK: CollectionView delegate
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
        let column = (indexPath as NSIndexPath).row
        let row = (indexPath as NSIndexPath).section
    
        print("\(column)  \(row)")
    }
    }
    
    class ViewController: UIViewController {
    let collectionView = CollectionView()
    
    override func viewDidLoad() {
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(collectionView)
        self.view.backgroundColor = UIColor.red
    
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
        self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView]))
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    
        collectionView.updateCollectionView()
    }
    }
    
    最后,您可以拥有精美的CollectionView


    关于noob的答案:

    func collectionView(collectionView: UICollectionView,
        layout collectionViewLayout: UICollectionViewLayout,
        sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    
            let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
            let totalSpace = flowLayout.sectionInset.left
                + flowLayout.sectionInset.right
                + (flowLayout.minimumInteritemSpacing * CGFloat(numberOfItemsPerRow - 1))
            let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(numberOfItemsPerRow))
            return CGSize(width: size, height: size)
    }
    

    这允许单元格之间存在任何间距。它假定一个名为
    numberOfItemsPerRow
    Int
    成员变量,并且所有单元格都是正方形,大小相同。正如jhilgert00的回答中所指出的,我们还必须对方向的变化做出反应,但现在使用
    视图willTransitionOnToSize
    ,因为
    willRotateToInterfaceOrientation
    被贬低了。

    如果您懒得使用委托

    extension UICollectionView {
        func setItemsInRow(items: Int) {
            if let layout = self.collectionViewLayout as? UICollectionViewFlowLayout {
                let contentInset = self.contentInset
                let itemsInRow: CGFloat = CGFloat(items);
                let innerSpace = layout.minimumInteritemSpacing * (itemsInRow - 1.0)
                let insetSpace = contentInset.left + contentInset.right + layout.sectionInset.left + layout.sectionInset.right
                let width = floor((CGRectGetWidth(frame) - insetSpace - innerSpace) / itemsInRow);
                layout.itemSize = CGSizeMake(width, width)
            }
        }
    }
    

    PS:也应在旋转后调用

    以下是Swift 3或更高版本的工作代码,以具有两列布局:

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        let nbCol = 2
        
        let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
        let totalSpace = flowLayout.sectionInset.left
            + flowLayout.sectionInset.right
            + (flowLayout.minimumInteritemSpacing * CGFloat(nbCol - 1))
        let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(nbCol))
        return CGSize(width: size, height: size)
    }
    

    您可以随意将“nbCol”更改为所需的列数。

    使用Swift 5和iOS 12.3,您可以使用以下4实现之一来设置
    UICollectionView
    中每行的项目数,同时管理插入和大小更改(包括旋转)


    #1.子类化
    UICollectionViewFlowLayout
    并使用
    UICollectionViewFlowLayout
    的属性 ColumnFlowLayout.swift:

    CollectionViewController.swift:


    #2.使用UICollectionViewFlowLayout的方法
    #3.使用UICollectionViewDelegateFlowLayout的方法
    #4.子类化
    UICollectionViewFlowLayout
    并使用
    UICollectionViewFlowLayout
    的属性 CollectionViewController.swift:

    FlowLayout.swift:


    完美的解决方案是 使用UICollectionViewDelegateFlowLayout 但是您可以很容易地计算单元格宽度,并根据所需的列数进行分割

    棘手的是使宽度没有分数

    (UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    
    {
     CGFloat screenWidth = self.view.frame.size.width;
       CGFloat marginWidth = (screenWidth - collectionView.frame.size.width);
    
    
       CGFloat cellWith = (collectionView.frame.size.width - marginWidth )/3;
       cellWith= floorf(cellWith);
    
    
    
    
      CGSize retval = CGSizeMake(cellWith,cellWith);
    
    
      return retval;}
    

    swift3.0。适用于水平和垂直滚动方向以及可变间距

    指定列数

    let numberOfColumns: CGFloat = 3
    
    配置
    flowLayout
    以呈现指定的
    numberOfColumns

    if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
        let horizontalSpacing = flowLayout.scrollDirection == .vertical ? flowLayout.minimumInteritemSpacing : flowLayout.minimumLineSpacing
        let cellWidth = (collectionView.frame.width - max(0, numberOfColumns - 1)*horizontalSpacing)/numberOfColumns
        flowLayout.itemSize = CGSize(width: cellWidth, height: cellWidth)
    }
    

    我只是想在Imanou Petit的回答后面加上一句#2。为了确保无论屏幕宽度如何,边距都是精确的,我使用了一个具有所需边距的迭代解算器,并将#列作为输入。我还添加了一个方向标志,用于将他们的最终利润与目标进行比较

    迭代解算器如下所示,返回cellWidth和margin

    private func iterativeCellSpacing(targetMargins : CGFloat,
                                      cellsPerRow : Int,
                                      isMinTarget : Bool) -> (CGFloat, CGFloat)
    {
        var w : CGFloat = 0
        var m : CGFloat = targetMargins
        let cols : CGFloat = CGFloat(cellsPerRow)
    
        let numMargins : CGFloat = cols + 1.0
        let screenWidth : CGFloat = collectionView!.bounds.size.width
    
    
        var delta = CGFloat.greatestFiniteMagnitude
        while abs(delta) > 0.001
        {
            let totalMarginSpacing = numMargins * m
            let totalCellSpacing = screenWidth - totalMarginSpacing
    
            if (isMinTarget)
            {
                w = floor(totalCellSpacing / cols)
                m = ceil((screenWidth - cols * w) / numMargins)
            }
            else
            {
                w = ceil(totalCellSpacing / cols)
                m = floor((screenWidth - cols * w) / numMargins)
            }
    
            delta = screenWidth - w * CGFloat(cellsPerRow) - m * numMargins
        }
    
        return (w, m)
    }
    
    我这样称呼它:

    fileprivate var margin: CGFloat = 20
    fileprivate var cellWidth : CGFloat = 80
    fileprivate let cellsPerRow = 4
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
    
        (cellWidth, margin) = iterativeCellSpacing(targetMargins: margin, cellsPerRow: 4, isMinTarget: true)
        ...
    }
    
    extension MyCollectionController : UICollectionViewDelegateFlowLayout
    
    然后,我将cellWidth和边距值应用于流布局,如下所示:

    fileprivate var margin: CGFloat = 20
    fileprivate var cellWidth : CGFloat = 80
    fileprivate let cellsPerRow = 4
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
    
        (cellWidth, margin) = iterativeCellSpacing(targetMargins: margin, cellsPerRow: 4, isMinTarget: true)
        ...
    }
    
    extension MyCollectionController : UICollectionViewDelegateFlowLayout
    
    { 功能集合
     #pragma mark Collection View Layout data source methods
    // collection view with autolayout
    
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
    {
    return 4;
    }
    
    - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
     {
     return 1;
    }
    
    - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
    return UIEdgeInsetsMake(4, 4, 4, 4);
    }
    - (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout *)collectionViewLayout
       sizeForItemAtIndexPath:(NSIndexPath *)indexPath
    {
     return 
     CGSizeMake(kCollectionViewCellWidth,kCollectionViewCellHieght);
     // CGSizeMake (kCollectionViewCellWidthHieght,kCollectionViewCellWidthHieght);
    }
    
    let margin: CGFloat = 10
    
     guard let collectionView = docsColl, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
    
        flowLayout.minimumInteritemSpacing = margin
        flowLayout.minimumLineSpacing = margin
        flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
        let noOfCellsInRow = 2   //number of column you want
        let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
        let totalSpace = flowLayout.sectionInset.left
            + flowLayout.sectionInset.right
            + (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))
    
        let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
        return CGSize(width: size, height: size)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
            var columnCount = 3
            let width  = (view.frame.width - 20) / columnCount
            return CGSize(width: width, height: width)
    }