Search code examples
swiftuiimageviewuicollectionviewcell

Collectionview cells are not showing according to screen width


cells are not showing in particular order i need 3 cells in a row so tried CGSize(width: UIScreen.main.bounds.width * 0.3, height: 100) but getting unrelated out put

code:

    class ViewController1: UIViewController {
    @IBOutlet weak var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let layOut = UICollectionViewFlowLayout()
    layOut.scrollDirection = .horizontal
    layOut.minimumLineSpacing = 5
    layOut.minimumInteritemSpacing = 5
    layOut.sectionInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
    layOut.itemSize = CGSize(width: UIScreen.main.bounds.width * 0.3, height: 100)

    collectionView.collectionViewLayout = layOut
    }
    }

    extension ViewController1: UICollectionViewDelegate, UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        10
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TestCollectionCell", for: indexPath) as! TestCollectionCell
        cell.layoutIfNeeded()
        return cell
    }
    }

Edit: got rounded cell

    class TestCollectionCell: UICollectionViewCell{
    @IBOutlet weak var roundImg: UIImageView!
    @IBOutlet weak var titleLbl: UILabel!
    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        roundImg.layer.cornerRadius = roundImg.frame.height / 2
        roundImg.contentMode = .scaleToFill
        roundImg.clipsToBounds = true
    }
    }

then i got this weird out put: here i need 3 cells in a row but here it shows weird out put, how to get 3 cells in

enter image description here

  [1]: https://i.sstatic.net/732AA.png

Solution

  • Couple things...

    First, there is likely some issues with your constraints, as every thing is shifted to the right. You haven't shown us anything about your cell layout, so tough to say.


    Second, during development, give your UI elements contrasting background colors to make it easy to see the framing.

    For example, this:

    enter image description here

    gives you a LOT more information than this:

    enter image description here


    Third, you almost certainly want layOut.scrollDirection = .vertical instead of layOut.scrollDirection = .horizontal.

    When set to horizontal scrolling, the collection view fills top-to-bottom, then left-to-right.

    So, if you have only 10 items (as in your image), it will look like this:

    enter image description here

    but you likely want to scroll vertically (when you have lots of cells), and you want it to look like this with only 10 items:

    enter image description here


    Fourth, don't use UIScreen.main.bounds.width ... if your app is running on an iPad in multi-tasking mode, you won't be even close to what you're going for.

    Instead, use the frame of the collection view -- but when doing that we need to wait until the views have been laid-out. A good place for that is in viewDidLayoutSubviews(), but we have to be careful since that will be called many times.

    So, add a class-level property:

    var cvWidth: CGFloat = -1.0
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
    
        // only do this if the collection view frame has changed
        if cvWidth != collectionView.frame.size.width {
            cvWidth = collectionView.frame.size.width
            if let fl = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
                // we have 5-points on each side from .sectionInset
                //  and 5-points between "columns"
                // so we want the cell width to be 1/3rd of the collection view width
                // minus the "space"
                let w = (cvWidth - 20.0) / 3.0
                fl.itemSize = .init(width: w, height: 100)
            }
        }
    }
    

    That will sort of work. We're dealing with floating-point though, so the values will not be exact, and we'll usually end up with only TWO columns fitting.

    To fix that, change:

        layOut.minimumInteritemSpacing = 5
    

    to:

        layOut.minimumInteritemSpacing = 0
    

    That will allow the collection view to fit 3 "cell columns" and add just enough spacing between the items.


    Edit - to make this a little more flexible, and allow for different number of columns...

    var cvWidth: CGFloat = -1.0
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if cvWidth != collectionView.frame.size.width {
            cvWidth = collectionView.frame.size.width
            if let fl = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
                
                // number of columns to show - you might be setting this elsewhere
                let nColumns: Int = 4
                
                // get the left and right section inset values
                let sideSpace: CGFloat = fl.sectionInset.left + fl.sectionInset.right
                
                // we want *about* 5-points spacing between items
                let totalEmptyspace: CGFloat = sideSpace + (CGFloat(nColumns) * 5.0)
                
                // calculate item width for nColumns number of columns
                let w = (cvWidth - totalEmptyspace) / CGFloat(nColumns)
                fl.itemSize = .init(width: w, height: 100)
    
                // make sure interItemSpacing is set to Zero!
                fl.minimumInteritemSpacing = 0.0
    
            }
        }
    }
    

    With nColumns = 4 we get this:

    enter image description here