如何在 Collecionview 中显示网格线 Swift

How to show grid lines in Collecionview Swift

我正在 Swift 使用自定义 collectionview 的应用程序。我想在其中显示列行,我已经实现了。 但是,我想在单元格内显示网格线。

下面是我的代码:

Viewcontroller class

import UIKit

private let reuseIdentifier = "cell"

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    
    @IBOutlet weak var collectionView: UICollectionView!

    var theData = [[String]]()
    override func viewDidLoad() {
        super.viewDidLoad()
        
        theData = [
            ["1", "Name", "TV", "LCD", "LED", ""],
            ["2", "Market", "No", "Charge", "Discount", ""],
            ["3", "value", "2.00", "05.00", "49.30", "200", ""],
            ["4", "Coupons", "1", "1","1","1","Total Price: "]]
        
        let layout = CustomLayout()
        collectionView?.collectionViewLayout = layout
        collectionView?.dataSource = self
        collectionView?.delegate = self
        layout.scrollDirection = .horizontal
        layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        layout.minimumInteritemSpacing = 10
        layout.minimumLineSpacing = 10
        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.showsHorizontalScrollIndicator = false

    }
    
    // MARK: UICollectionViewDataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return theData[section].count
    }
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return theData.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CustomCollectionViewCell
        cell?.myLabel.text = theData[indexPath.section][indexPath.row]
        return cell!
    }
}

自定义collectionviewflowlayout class

import UIKit

class CustomLayout: UICollectionViewFlowLayout {

    let  itemWidth = 200
    let itemHeight = 200

   func collectionViewContentSize() -> CGSize {
       
        let xSize = (collectionView?.numberOfItems(inSection: 0))! * (itemWidth + 2) // the 2 is for spacing between cells.
        let ySize = collectionView!.numberOfSections * (itemHeight + 2)
        return CGSize(width: xSize, height: ySize)
    }
    
    override func layoutAttributesForItem(at path: IndexPath?) -> UICollectionViewLayoutAttributes? {
        
        var attributes: UICollectionViewLayoutAttributes? = nil
        if let path = path {
            attributes = UICollectionViewLayoutAttributes(forCellWith: path)
            var xValue: Int
            attributes?.size = CGSize(width: itemWidth, height: itemHeight)
            xValue = itemWidth / 2 + (path.row ) * (itemWidth + 2)
            let yValue = itemHeight + (path.section ) * (itemHeight + 2)
            attributes?.center =  CGPoint(x:CGFloat(xValue), y:CGFloat(yValue))
        }
        return attributes
   }
    
    func layoutAttributesForElements(in rect: CGRect) -> [AnyHashable]? {
        
        let minRow = Int((rect.origin.x > 0) ? Int(rect.origin.x) / (itemWidth + 2) : 0) // need to check because bounce gives negative values  for x.

        let maxRow = Int(Int(rect.size.width) / (itemWidth + 2) + minRow)
        var attributes: [AnyHashable] = []
        for i in 0..<(self.collectionView?.numberOfSections)! {
            for j in (minRow..<maxRow) {
                let indexPath = IndexPath(item: j, section: i)
                attributes.append(layoutAttributesForItem(at: indexPath))
            }
        }
        return attributes
    }
}

此外,我只想更改 header 标题 (1,2,3,4) 的背景颜色。

有什么建议吗?

我想像下面的截图网格线一样显示

如果您要使用“固定网格”——即 x-columns by y-rows - 您可以在 prepare() 中计算单元格布局,保存 cellAttributes 在数组中,然后将该数组用于 layoutAttributesForElements(in rect: CGRect):

class CustomLayout: UICollectionViewLayout {
    private var computedContentSize: CGSize = .zero
    private var cellAttributes = [IndexPath: UICollectionViewLayoutAttributes]()

    let itemWidth = 100
    let itemHeight = 60

    let gridLineWidth = 1
    
    override func prepare() {
        
        guard let collectionView = collectionView else {
            fatalError("not a collection view?")
        }
        
        // Clear out previous results
        computedContentSize = .zero
        cellAttributes = [IndexPath: UICollectionViewLayoutAttributes]()
        
        let numItems = collectionView.numberOfItems(inSection: 0)
        let numSections = collectionView.numberOfSections

        let widthPlusGridLineWidth = itemWidth + gridLineWidth
        let heightPlusGridLineWidth = itemHeight + gridLineWidth
        
        for section in 0 ..< numSections {
            for item in 0 ..< numItems {
                let itemFrame = CGRect(x: item * widthPlusGridLineWidth + gridLineWidth,
                                       y: section * heightPlusGridLineWidth + gridLineWidth,
                                       width: itemWidth, height: itemHeight)
                
                let indexPath = IndexPath(item: item, section: section)
                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
                attributes.frame = itemFrame
                
                cellAttributes[indexPath] = attributes
            }
        }
        
        computedContentSize = CGSize(width: numItems * widthPlusGridLineWidth + gridLineWidth, height: numSections * heightPlusGridLineWidth + gridLineWidth) // Store computed content size
    }
    
    override var collectionViewContentSize: CGSize {
        return computedContentSize
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var attributeList = [UICollectionViewLayoutAttributes]()
        
        for (_, attributes) in cellAttributes {
            if attributes.frame.intersects(rect) {
                attributeList.append(attributes)
            }
        }
        
        return attributeList
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cellAttributes[indexPath]
    }
    
}

你的控制器 class 然后变成:

class OutlinedGridViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    
    private let reuseIdentifier = "cell"
    
    @IBOutlet var collectionView: UICollectionView!
    
    var theData = [[String]]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // set view background to systemYellow so we can see the
        //  collection view frame
        view.backgroundColor = .systemYellow
        
        theData = [
            ["1", "Name", "TV", "LCD", "LED", "", ""],
            ["2", "Market", "No", "Charge", "Discount", "", ""],
            ["3", "value", "2.00", "05.00", "49.30", "200", ""],
            ["4", "Coupons", "1", "1","1","1","Total Price: "]]
        
        let layout = CustomLayout()
        collectionView.collectionViewLayout = layout
        collectionView.dataSource = self
        collectionView.delegate = self
        
        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.showsHorizontalScrollIndicator = false
        
        collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
        
        collectionView.backgroundColor = .black
    }

    
    // MARK: UICollectionViewDataSource
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return theData.count
    }
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return theData[0].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CustomCollectionViewCell
        
        let d = theData[indexPath.item]
        cell.myLabel.text = d[indexPath.section]

        // set background color for top row, else use white
        cell.contentView.backgroundColor = indexPath.section == 0 ? .yellow : .white

        return cell
    }

}

并且,使用此自定义单元格(单个居中标签):

class CustomCollectionViewCell: UICollectionViewCell {
    
    var myLabel = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        myLabel.textColor = .black
        myLabel.textAlignment = .center
        myLabel.font = .systemFont(ofSize: 14.0)
        myLabel.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(myLabel)
        
        let g = contentView
        NSLayoutConstraint.activate([
            
            myLabel.leadingAnchor.constraint(greaterThanOrEqualTo: g.leadingAnchor, constant: 4.0),
            myLabel.trailingAnchor.constraint(lessThanOrEqualTo: g.trailingAnchor, constant: -4.0),
            myLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            myLabel.centerYAnchor.constraint(equalTo: g.centerYAnchor),

        ])
    }
    
}

我们得到了这个(我把集合视图做得比需要的略小,所以我们可以看到水平和垂直滚动):

您没有解释要对“总价”行做什么...但是如果您的目的是减少最后一行的列数,那么修改这段代码对您来说是一个很好的练习: )