HorizontalCollectionView Content width and spacing

2019-02-20 01:30发布

问题:

How can I make my horizontal collection view labels width to wrap the content width of the label and make them have equal spacing between each of them? Currently I have the collection view cell width as 100. If I increase the width to fit other labels, the shorter labels have more spacing between them. Any ideas will be appreciated.

回答1:

Calculate the width of the label text first with the font associated with the text.

extension String {
    func size(with font: UIFont) -> CGSize {
        let fontAttribute = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttribute)
        return size
    }
}

Return the calculated width along with collectionView height in collectionView(_, collectionViewLayout:_, sizeForItemAt).

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let newWidth = titles[indexPath.row].size(with: labelFont!).width + 10 //Added 10 to make the label visibility very clear
    return CGSize(width: newWidth, height: collectionView.bounds.height)
}

Entire source code:

class ViewController: UIViewController {

    @IBOutlet weak var collection: UICollectionView!

    let labelFont = UIFont(name: "Helvetica Neue", size: 18)
    let titles = ["Hi", "Hello", "HorizontalCollectionView", "VerticalCollectionView"]

    override func viewDidLoad() {

        super.viewDidLoad()
        collection.backgroundColor = UIColor(red: 68/255, green: 143/255, blue: 1, alpha: 1)
        collection.register(UINib.init(nibName: "CustomCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CustomCollectionViewCell")
    }
}

extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        return titles.count
    }

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

        return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    }

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

        return 0
    }

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

        return 10 // Adjust the inter item space based on the requirement.
    }

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let newWidth = titles[indexPath.row].size(with: labelFont!).width + 10 //Added 10 to make the label visibility very clear
    return CGSize(width: newWidth, height: collectionView.bounds.height)
}

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionViewCell", for: indexPath) as! CustomCollectionViewCell
        cell.titleLabel.text = titles[indexPath.row]
        cell.titleLabel.font = labelFont!
        return cell
    }
}

extension String {
    func size(with font: UIFont) -> CGSize {
        let fontAttribute = [NSAttributedString.Key.font: font]
        let size = self.size(withAttributes: fontAttribute)
        return size
    }
}

Another Solution:

ViewController.swift

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {

    @IBOutlet weak var collView: UICollectionView!


    var tasksArray = ["To Do", "SHOPPING","WORK"]
    var selectedIndex = Int()

    override func viewDidLoad() {
        super.viewDidLoad()
        collView.register(UINib.init(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "Cell")
        let layout = collView?.collectionViewLayout as! UICollectionViewFlowLayout
        layout.itemSize = UICollectionViewFlowLayout.automaticSize
        layout.estimatedItemSize = CGSize(width: 170, height: 50)
        // Do any additional setup after loading the view, typically from a nib.
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return tasksArray.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
        cell.lblName.text = tasksArray[indexPath.row]
        if selectedIndex == indexPath.row
        {
            cell.backgroundColor = UIColor.lightGray
        }
        else
        {
            cell.backgroundColor = UIColor.white
        }
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        selectedIndex = indexPath.row
        self.collView.reloadData()
    }
}

Cell.swift

class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var lblName: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        layer.borderWidth = 1
        layer.cornerRadius = bounds.height / 2
    }

    override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
        setNeedsLayout()
        layoutIfNeeded()
        let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
        var frame = layoutAttributes.frame
        frame.size.width = ceil(size.width)
        layoutAttributes.frame = frame
        return layoutAttributes
    }
}


回答2:

You have to manually calculate the width of each cell. To calculate the width, you actually calculate the width of the text residing in that cell.

use following

 boundingRect(with:options:attributes:context:)

https://developer.apple.com/documentation/foundation/nsstring/1524729-boundingrect

to calculate the width of a given string. So after calculating the returning the size the cell should be wrapping the text.

Now come the second part, equal spacing between items. use interItemSpacing to define the spacing between each cell.