Home > Software engineering >  How to make horizontal CollectionView size fit its content programatically
How to make horizontal CollectionView size fit its content programatically

Time:08-31

I have a collection view which takes up the whole screen and should scroll horizontally.

Problem is I can't adjust the size for item while retaining the carousel effect. If I lower the size for item, the cell stacks vertically just like here:

enter image description here

The pink background is the collectionView. The collectionView fills the viewController's view that is why the cells doesn't align horizontally.

The alignment should be like this which scrolls horizontally much like a carousel but the problem is it's too big and it took up the whole view.

enter image description here

What's expected is for the cell and collection view to be around 100 height or 200 height only and can still scroll horizontally.

This is my existing code:

I am using SnapKit for constraints.

ViewController

import UIKit
import SnapKit

class ViewController: UIViewController {
    
    
    let viewModel: [SeatViewModel] = [.init(text: "single text"), .init(text: "slightly longer text."), .init(text: "very very very very long long text")]
    
    private lazy var collectionView: UICollectionView = {
      let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
      let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
      cv.delegate = self
      cv.backgroundColor = .white
      cv.register(SeatCardCell.self, forCellWithReuseIdentifier: "SeatCardCell")
      return cv
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(collectionView)
        
        collectionView.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.width.equalTo(200)
            make.height.equalTo(100)
        }
        collectionView.dataSource = self
        collectionView.backgroundColor = .systemPink
    }
    

    
    func lines(label: UILabel) -> Int {
        self.collectionView.layoutIfNeeded()
        let textSize = CGSize(width: label.frame.size.width, height: CGFloat(Float.infinity))
        let rHeight = lroundf(Float(label.sizeThatFits(textSize).height))
        let charSize = lroundf(Float(label.font.lineHeight))
        let lineCount = rHeight/charSize
        return lineCount
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        collectionView.frame = view.bounds
    }
    
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let collectioCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SeatCardCell", for: indexPath) as? SeatCardCell else { fatalError() }
        collectioCell.backgroundColor = [UIColor.green, .red, .black, .brown, .yellow].randomElement()
        collectioCell.viewModel = viewModel[indexPath.row]
        return collectioCell
    }
    
}


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

            return CGSize(width: 200, height: self.collectionView.frame.height)
        }

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

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

View Model and Cell

import UIKit
import SnapKit

struct SeatViewModel {
    let text: String
}

class SeatCardCell: UICollectionViewCell {
    
    var viewModel: SeatViewModel? {
        didSet {
            configure()
        }
    }

    // MARK: - Properties

    let seatImageView = UIImageView(image: nil)
    
    let seatLabel: CustomLabel = {
        let label = CustomLabel()
        return label
    }()

    // MARK: - Lifecycle

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    
    private func configure() {
        guard let viewModel = viewModel else { return }
        seatLabel.text = viewModel.text
        seatLabel.backgroundColor = .cyan
        seatLabel.sizeToFit()
        seatImageView.image = .strokedCheckmark
        
        let stackView = UIStackView(arrangedSubviews: [seatImageView, seatLabel])
        stackView.spacing = 0
        stackView.alignment = .center
        
        addSubview(stackView)
        
        seatImageView.snp.makeConstraints { make in
            make.size.equalTo(40)
        }
        
        stackView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
}


// MARK: - CUSTOM TITLE LABEL
class CustomLabel: UILabel {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        self.textColor = .darkGray
        self.textAlignment = .left
        self.numberOfLines = 4
    }
}

CodePudding user response:

The problem of your code is you set again the collection view frame equal to view frame in viewDidLayoutSubviews.

You just need to remove it and your code will work perfectly fine.

override func viewDidLayoutSubviews() {
     super.viewDidLayoutSubviews()
     // remove it
     //collectionView.frame = view.bounds
}

Final result

  • Related