Search code examples
iosswiftuicollectionviewuicollectionviewcell

How to fix UICollectionViewCells sometimes not loading in?


I am trying to make a little horizontal UICollectionView in the top section of my app but every time I load the app there are always like 2 or 3 cells that don't load its content. And the cells that don't load are changing.

Here is what I have done:

public var collectionViewDates = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(collectionViewDates)
    collectionViewDates.frame = CGRect(x: 0,
                                       y: 90,
                                       width: view.width,
                                       height: 50)
}

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 30
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

         let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DatesCollectionViewCell.identifier, for: indexPath) as! DatesCollectionViewCell
         cell.dateLabel.text = "I dag\(indexPath.row)"
         return cell
        
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DatesCollectionViewCell.identifier, for: indexPath) as! DatesCollectionViewCell
        return CGSizeMake(cell.dateLabel.width + 30, collectionView.height)
        
    }


And in the cell:

public var dateLabel: UILabel = {
        let label = UILabel()
        label.text = "I dag"
        label.adjustsFontSizeToFitWidth = true
        label.font = .systemFont(ofSize: 20, weight: .bold)
        label.backgroundColor = .systemBlue
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(dateLabel)
        
        dateLabel.frame = CGRect(x: 0,
                                 y: 0,
                                 width: 50,
                                 height: contentView.height)
        
        dateLabel.center.y = contentView.center.y
        dateLabel.center.x = contentView.center.x
        
        
        
        
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

I don't understand why the cells sometimes load because I have done this before with other horizontal UICollectionViews. But this time I was not so lucky.

Here is an image of the problem: Picture of my horizontal UICollectionView missing a cell


Solution

  • First, spend a little time learning about auto-layout.

    Second, if you want 50x50 cells, set the .itemSize on the collection view's flow layout.

    Take a look at this:

    class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
        
        public var collectionViewDates: UICollectionView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let fl = UICollectionViewFlowLayout()
            fl.scrollDirection = .horizontal
            fl.minimumLineSpacing = 24.0
            fl.minimumInteritemSpacing = 24.0
            fl.itemSize = .init(width: 50.0, height: 50.0)
            
            collectionViewDates = UICollectionView(frame: .zero, collectionViewLayout: fl)
            
            collectionViewDates.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(collectionViewDates)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                collectionViewDates.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                collectionViewDates.leadingAnchor.constraint(equalTo: g.leadingAnchor),
                collectionViewDates.trailingAnchor.constraint(equalTo: g.trailingAnchor),
                collectionViewDates.heightAnchor.constraint(equalToConstant: 64.0),
            ])
            
            collectionViewDates.register(DatesCollectionViewCell.self, forCellWithReuseIdentifier: DatesCollectionViewCell.identifier)
            collectionViewDates.dataSource = self
            collectionViewDates.delegate = self
            
            collectionViewDates.backgroundColor = .black
        }
        
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 30
        }
        
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DatesCollectionViewCell.identifier, for: indexPath) as! DatesCollectionViewCell
            cell.dateLabel.text = "I dag\(indexPath.row)"
            return cell
            
        }
        
    }
    
    class DatesCollectionViewCell: UICollectionViewCell {
        static let identifier: String = "datesCell"
        
        public var dateLabel: UILabel = {
            let label = UILabel()
            label.text = "I dag"
            label.adjustsFontSizeToFitWidth = true
            label.font = .systemFont(ofSize: 20, weight: .bold)
            label.backgroundColor = .systemBlue
            label.textColor = .white
            return label
        }()
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            commonInit()
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            commonInit()
        }
        
        private func commonInit() {
    
            dateLabel.translatesAutoresizingMaskIntoConstraints = false
            contentView.addSubview(dateLabel)
            
            NSLayoutConstraint.activate([
                dateLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
                dateLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
                dateLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
                dateLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            ])
    
        }
    }
    

    Result:

    enter image description here

    enter image description here

    enter image description here


    Edit

    If you want the cells to size to fit the widths of the labels...

    Give the flow layout an estimated item size:

    //fl.itemSize = .init(width: 50.0, height: 50.0)
    fl.estimatedItemSize = .init(width: 50.0, height: 50.0)
    

    and don't set the label to autoshrink the font:

    //label.adjustsFontSizeToFitWidth = true
    

    enter image description here

    enter image description here