UICollectionView Set number of columns

后端 未结 17 1125
执笔经年
执笔经年 2020-11-28 17:28

I just started learning about UICollectionViews. I\'m wondering if anyone knows how to specify the number of columns in a collectionview. The default is set to 3 (iPhone/por

相关标签:
17条回答
  • 2020-11-28 18:28

    Updated to Swift 3:

    Instead of the flow layout, I prefer using custom layout for specific column number and row number. Because:

    1. It can be dragged horizontally if column number is very big.
    2. It is more acceptable logically because of using column and row.

    Normal cell and Header cell: (Add UILabel as a IBOutlet to your 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
    }
    }
    

    Custom layout:

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

    Use CollectionView from ViewController:

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

    Finally you can have fancy CollectionView!

    0 讨论(0)
  • 2020-11-28 18:29

    First add UICollectionViewDelegateFlowLayout as protocol.

    Then:

    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)
    }
    
    0 讨论(0)
  • 2020-11-28 18:31

    I just wanted to append to Imanou Petit's answer #2. To ensure margins are exact regardless of screen width, I use an iterative solver with a desired margin, and the # of columns as inputs. I also added a directional flag on where their final margins will be compared to their target.

    The iterative solver is shown below and returns cellWidth and 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)
    }
    

    I call it as such:

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

    I then apply the cellWidth and margin values to the flow layout as such:

    extension MyCollectionController : UICollectionViewDelegateFlowLayout
    

    { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: cellWidth, height: cellWidth) }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
    {
        return UIEdgeInsetsMake(margin, margin, margin, margin)
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat
    {
        return margin
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
    {
        return margin
    }
    

    }

    Hope this helps. There probably is an easier way to ensure margins are exact, but this is one method. Also, this code hasn't been tested for devices that allow rotating collectionviews.

    Thanks,

    0 讨论(0)
  • 2020-11-28 18:33

    With Swift 5 and iOS 12.3, you can use one the 4 following implementations in order to set the number of items per row in your UICollectionView while managing insets and size changes (including rotation).


    #1. Subclassing UICollectionViewFlowLayout and using UICollectionViewFlowLayout's itemSize property

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


    #2. Using UICollectionViewFlowLayout's itemSize method

    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. Using UICollectionViewDelegateFlowLayout's collectionView(_:layout:sizeForItemAt:) method

    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. Subclassing UICollectionViewFlowLayout and using UICollectionViewFlowLayout's estimatedItemSize property

    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 = UICollectionViewFlowLayout.automaticSize
        }
    
        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(UIView.layoutFittingCompressedSize).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(UIView.layoutFittingCompressedSize).height
            return layoutAttributes
        }
        */
    
    }
    

    0 讨论(0)
  • 2020-11-28 18:33

    Expanding on noob's answer:

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

    This allows for any spacing between the cells. It assumes an Int member variable called numberOfItemsPerRow and also that all the cells are square and the same size. As noted in jhilgert00's answer we must also react to orientation changes, but now by using viewWillTransitionToSize as willRotateToInterfaceOrientation is depreciated.

    0 讨论(0)
提交回复
热议问题