UICollectionView设置列数

时间:2013-02-03 17:12:29

标签: ios objective-c xcode uicollectionview uicollectionviewcell

我刚开始学习UICollectionViews。我想知道是否有人知道如何指定集合视图中的列数。默认设置为3(iPhone /肖像)。我查看了文档,似乎无法找到简明的答案。

17 个答案:

答案 0 :(得分:107)

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

#1。子类化UICollectionViewFlowLayout并使用UICollectionViewFlowLayout的{​​{3}}属性

ColumnFlowLayout.swift:

import UIKit

class ColumnFlowLayout: UICollectionViewFlowLayout {

    let cellsPerRow: Int

    init(cellsPerRow: Int, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        self.cellsPerRow = cellsPerRow
        super.init()

        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
    }

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

    override func prepare() {
        super.prepare()

        guard let collectionView = collectionView else { return }
        let marginsAndInsets = sectionInset.left + sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
        let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
        itemSize = CGSize(width: itemWidth, height: itemWidth)
    }

    override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
        let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
        context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
        return context
    }

}

CollectionViewController.swift:

import UIKit

class CollectionViewController: UICollectionViewController {

    let columnLayout = ColumnFlowLayout(
        cellsPerRow: 5,
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView?.collectionViewLayout = columnLayout
        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 59
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.orange
        return cell
    }

}

itemSize

#2。使用UICollectionViewFlowLayout的{​​{3}}方法

import UIKit

class CollectionViewController: UICollectionViewController {

    let margin: CGFloat = 10
    let cellsPerRow = 5

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let collectionView = collectionView, let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return }

        flowLayout.minimumInteritemSpacing = margin
        flowLayout.minimumLineSpacing = margin
        flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)

        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func viewWillLayoutSubviews() {
        guard let collectionView = collectionView, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }
        let marginsAndInsets = flowLayout.sectionInset.left + flowLayout.sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + flowLayout.minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
        let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
        flowLayout.itemSize =  CGSize(width: itemWidth, height: itemWidth)
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 59
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.orange
        return cell
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }

}

#3。使用UICollectionViewDelegateFlowLayout的{​​{3}}方法

import UIKit

class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

    let inset: CGFloat = 10
    let minimumLineSpacing: CGFloat = 10
    let minimumInteritemSpacing: CGFloat = 10
    let cellsPerRow = 5

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return minimumLineSpacing
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return minimumInteritemSpacing
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let marginsAndInsets = inset * 2 + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
        let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
        return CGSize(width: itemWidth, height: itemWidth)
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 59
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.orange
        return cell
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }

}

#4。子类化UICollectionViewFlowLayout并使用UICollectionViewFlowLayout的{​​{3}}属性

CollectionViewController.swift:

import UIKit

class CollectionViewController: UICollectionViewController {

    let items = [
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
        "Lorem ipsum dolor sit amet, consectetur.",
        "Lorem ipsum dolor sit amet.",
        "Lorem ipsum dolor sit amet, consectetur.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing.",
        "Lorem ipsum.",
        "Lorem ipsum dolor sit amet.",
        "Lorem ipsum dolor sit.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
        "Lorem ipsum dolor sit amet, consectetur."
    ]

    let columnLayout = FlowLayout(
        cellsPerRow: 3,
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )

    override func viewDidLoad() {
        super.viewDidLoad()

        collectionView?.collectionViewLayout = columnLayout
        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.register(Cell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return items.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
        cell.label.text = items[indexPath.row]
        return cell
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }

}

FlowLayout.swift:

import UIKit

class FlowLayout: UICollectionViewFlowLayout {

    let cellsPerRow: Int

    required init(cellsPerRow: Int = 1, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        self.cellsPerRow = cellsPerRow

        super.init()

        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
        estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
    }

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

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else { return nil }
        guard let collectionView = collectionView else { return layoutAttributes }

        let marginsAndInsets = collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + sectionInset.left + sectionInset.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
        layoutAttributes.bounds.size.width = ((collectionView.bounds.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)

        return layoutAttributes
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let superLayoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
        guard scrollDirection == .vertical else { return superLayoutAttributes }

        let layoutAttributes = superLayoutAttributes.compactMap { layoutAttribute in
            return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute
        }

        // (optional) Uncomment to top align cells that are on the same line
        /*
        let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
        for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
            guard let max = attributes.max(by: { $0.size.height < $1.size.height }) else { continue }
            for attribute in attributes where attribute.size.height != max.size.height {
                attribute.frame.origin.y = max.frame.origin.y
            }
        }
         */

        // (optional) Uncomment to bottom align cells that are on the same line
        /*
        let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })
        for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
            guard let max = attributes.max(by: { $0.size.height < $1.size.height }) else { continue }
            for attribute in attributes where attribute.size.height != max.size.height {
                attribute.frame.origin.y += max.frame.maxY - attribute.frame.maxY
            }
        }
         */

        return layoutAttributes
    }

}

Cell.swift:

import UIKit

class Cell: UICollectionViewCell {

    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)

        label.numberOfLines = 0
        backgroundColor = .orange
        contentView.addSubview(label)

        label.translatesAutoresizingMaskIntoConstraints = false
        label.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor).isActive = true
        label.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor).isActive = true
        label.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor).isActive = true
        label.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor).isActive = true
    }

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

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        layoutIfNeeded()
        label.preferredMaxLayoutWidth = label.bounds.size.width
        layoutAttributes.bounds.size.height = contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        return layoutAttributes
    }

    // Alternative implementation
    /*
    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        label.preferredMaxLayoutWidth = layoutAttributes.size.width - contentView.layoutMargins.left - contentView.layoutMargins.right
        layoutAttributes.bounds.size.height = contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        return layoutAttributes
    }
    */

}

enter image description here

答案 1 :(得分:76)

CollectionViews非常强大,而且价格合理。很多,有很多选择。正如omz所说:

  

您可以通过多种方式更改列数

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

  • collectionView:layout:insetForSectionAtIndex:
  • collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
  • collectionView:layout:minimumLineSpacingForSectionAtIndex:
  • collectionView:layout:referenceSizeForFooterInSection:
  • collectionView:layout:referenceSizeForHeaderInSection:
  • collectionView:layout:sizeForItemAtIndexPath:

此外,实现以下方法将强制您的UICollectionView在方向更改时更新它的布局:(假设您想为景观重新调整单元格的大小并使它们伸展)

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                               duration:(NSTimeInterval)duration{

    [self.myCollectionView.collectionViewLayout invalidateLayout];
}

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

http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12

http://skeuo.com/uicollectionview-custom-layout-tutorial

答案 2 :(得分:59)

我在UICollectionViewDelegateFlowLayout上实施了UICollectionViewController并覆盖了负责确定Cell大小的方法。然后我拿出屏幕宽度并按照我的列要求进行划分。例如,我想在每个屏幕尺寸上有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;
}

答案 3 :(得分:39)

扩大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)
}

这允许细胞之间的任何间隔。它假定Int成员变量名为numberOfItemsPerRow,并且所有单元格都是正方形且大小相同。正如jhilgert00的回答中所述,我们还必须对方向更改做出反应,但现在使用viewWillTransitionToSize作为willRotateToInterfaceOrientation会被折旧。

答案 4 :(得分:12)

以下是Swift 3的工作代码,它具有两列布局:

{{1}}

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

答案 5 :(得分:8)

已更新至Swift 3:

我更喜欢使用特定列号和行号的自定义布局,而不是流布局。这是因为:

  1. 如果列数非常大,可以水平拖动。
  2. 由于使用了列和行,因此逻辑上更可接受。
  3. 正常单元格和标题单元格:(将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
    }
    }
    

    <强>的CollectionView:

    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)")
    }
    }
    

    使用ViewController中的CollectionView:

    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!

    enter image description here

答案 6 :(得分:7)

由于UICollectionView非常灵活,因此您可以通过多种方式更改列数,具体取决于您使用的布局类型。

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

答案 7 :(得分:6)

如果你懒惰使用委托。

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:旋转后也应该调用

答案 8 :(得分:6)

已更新至Swift 5+ iOS 13

Collectionview估算大小必须

enter image description here

声明单元格的边距

let margin: CGFloat = 10

在viewDidLoad中配置minimumInteritemSpacingminimumLineSpacingsectionInset

 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)

UICollectionViewDataSource 方法sizeForItemAt

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)
}

答案 9 :(得分:3)

Swift 3.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)
}

答案 10 :(得分:2)

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

现在会问你将如何做到这一点?如果你不想打扰细胞框架,你可以调整

 collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
 collectionView:layout:minimumLineSpacingForSectionAtIndex:

另一种方法是为您提供自己的细胞位置。通过覆盖下面的两个方法,这些方法将在布局形成期间调用。

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

UICollectionViewLayoutAttributes是一个处理单元格位置,框架,Zindex等的类

答案 11 :(得分:2)

这个答案适用于swift 3.0 - &gt;

首先遵守协议:

 UICollectionViewDelegateFlowLayout

然后添加以下方法:

    //this method is for the size of items      
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = collectionView.frame.width/3
        let height : CGFloat = 160.0
        return CGSize(width: width, height: height)
    }
    //these methods are to configure the spacing between items

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsetsMake(0,0,0,0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }

答案 12 :(得分:1)

我只想附加Imanou Petit的回答#2。无论屏幕宽度如何,为了确保边距是精确的,我使用具有所需边距的迭代求解器和作为输入的列数。我还在其最终边距将与目标进行比较的位置添加了方向标记。

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

var no = 1
var numberOfRow = 5
for i in 1...numberOfRow {
    for _ in 1..<(6-i) {
        print(" ", terminator: " ")
    }
    for j in 1...i {
        print("\(j)", terminator: " ")
        no = j
    }
    for k in 1..<no {
        no -= 1
        print("\(no)", terminator: " ")
    }
    print(" ")
}

我称之为:

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)
}

然后我将cellWidth和margin值应用于流布局:

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)
    ...
}

{     func collectionView(_ collectionView:UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAt indexPath:IndexPath) - &gt; CGSize     {         返回CGSize(width:cellWidth,height:cellWidth)     }

extension MyCollectionController : UICollectionViewDelegateFlowLayout

}

希望这会有所帮助。可能有一种更简单的方法来确保边距是准确的,但这是一种方法。此外,此代码尚未针对允许旋转收集视图的设备进行测试。

谢谢,

答案 13 :(得分:0)

完美的解决方案是 使用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;}

答案 14 :(得分:0)

我制作了一个集合布局。

要使分隔符可见,请将集合视图的背景颜色设置为灰色。 每个部分一行。

用途:

let layout = GridCollectionViewLayout()
layout.cellHeight = 50 // if not set, cellHeight = Collection.height/numberOfSections
layout.cellWidth = 50  // if not set, cellWidth = Collection.width/numberOfItems(inSection)
collectionView.collectionViewLayout = layout

布局:

import UIKit

class GridCollectionViewLayout: UICollectionViewLayout {


var cellWidth : CGFloat = 0
var cellHeight : CGFloat = 0
var seperator: CGFloat = 1

private var cache = [UICollectionViewLayoutAttributes]()



override func prepare() {

    guard let collectionView = self.collectionView else {
        return
    }

    self.cache.removeAll()



        let numberOfSections = collectionView.numberOfSections

        if cellHeight <= 0
        {
            cellHeight = (collectionView.bounds.height - seperator*CGFloat(numberOfSections-1))/CGFloat(numberOfSections)
        }

        for section in 0..<collectionView.numberOfSections {

            let numberOfItems = collectionView.numberOfItems(inSection: section)

            let cellWidth2 : CGFloat
            if cellWidth <= 0
            {
                cellWidth2 = (collectionView.bounds.width - seperator*CGFloat(numberOfItems-1))/CGFloat(numberOfItems)
            }
            else
            {
                cellWidth2 = cellWidth
            }


            for row in 0..<numberOfItems {


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

                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
                attributes.frame = CGRect(x: (cellWidth2+seperator)*CGFloat(row),
                                          y: (cellHeight+seperator)*CGFloat(section),
                                          width: cellWidth2,
                                          height: cellHeight)

                //row_temp.append(attributes)

                self.cache.append(attributes)
            }
            //self.itemAttributes.append(row_temp)
        }

}

override var collectionViewContentSize: CGSize {

    guard let collectionView = collectionView else
    {
        return CGSize.zero
    }

    if (collectionView.numberOfSections <= 0)
    {
        return collectionView.bounds.size
    }

    let width:CGFloat
    if cellWidth <= 0
    {
        width = collectionView.bounds.width
    }
    else
    {
        width = cellWidth*CGFloat(collectionView.numberOfItems(inSection: 0))
    }

    let numberOfSections = CGFloat(collectionView.numberOfSections)
    var height:CGFloat = 0
    height += numberOfSections * cellHeight
    height += (numberOfSections - 1) * seperator



    return CGSize(width: width, height: height)
}

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

    var layoutAttributes = [UICollectionViewLayoutAttributes]()

    for attributes in cache {
        if attributes.frame.intersects(rect) {
            layoutAttributes.append(attributes)
        }
    }
    return layoutAttributes
}

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
    return cache[indexPath.item]
}
}

答案 15 :(得分:0)

试试这个,它对我来说非常适合,

按照以下步骤操作:

<强> 1。在.h文件中定义值。

     #define kNoOfColumsForCollection 3
     #define kNoOfRowsForCollection 4
     #define kcellSpace 5
     #define kCollectionViewCellWidth (self.view.frame.size.width - kcellSpace*kNoOfColumsForCollection)/kNoOfColumsForCollection
     #define kCollectionViewCellHieght (self.view.frame.size.height-40- kcellSpace*kNoOfRowsForCollection)/kNoOfRowsForCollection

  #define kNoOfColumsForCollection 3
  #define kCollectionViewCellWidthHieght (self.view.frame.size.width - 6*kNoOfColumsForCollection)/kNoOfColumsForCollection

2.在集合中添加代码查看布局数据源方法,如下所示

 #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);
}

希望这对某些人有帮助。

答案 16 :(得分:0)

首先添加 UICollectionViewDelegateFlowLayout 作为协议。

然后:

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)
}