user-44651
user-44651

Reputation: 4124

How to get all UICollectionViewCells to share the same height?

I have a UICollectionView where the Cell's do not fill their vertical space. What adjustment should I make to ensure each cell fills up the entire cell area? Or at least all share the same height for the row they are on?

enter image description here

Here is the Storyboard

enter image description here

UICollectionViewFlowLayout

class AddServiceFlowLayout: UICollectionViewFlowLayout {
    let cellsPerRow: Int
    
    init(cellsPerRow: Int, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        self.cellsPerRow = cellsPerRow
        super.init()
        
        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func prepare() {
        super.prepare()
        
        guard let collectionView = collectionView else { return }
        let marginsAndInsets = sectionInset.left + sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1)
        let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down)
        itemSize = CGSize(width: itemWidth, height: itemWidth)
    }
    
    override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext {
        let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext
        context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size
        return context
    }
    
}

UICollectionViewCell

class AddServiceViewCell: UICollectionViewCell {

    @IBOutlet weak var labelStackView: UIStackView!
    @IBOutlet weak var tvServiceName: UILabel!
    @IBOutlet weak var tvQuantityNeeded: UILabel!
    
    public var onCellTapped: (() -> ())?

    override func awakeFromNib() {
        super.awakeFromNib()
        self.layer.cornerRadius = 10.0
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        self.tvServiceName.text = nil
        self.tvQuantityNeeded.show()
    }
    
    public static func nib() -> UINib {
        return UINib.init(nibName: identifier, bundle: Bundle(for: ServiceLineItemCell.self))
    }
    
    public func configure(with service: TowService){
        self.tvServiceName.text = service.description
        
        if(service.calculated){
            self.tvQuantityNeeded.hide()
        }
    }
    
    public func eventTriggered()
    {
        onCellTapped?()
    }

}

UIViewController

class AddServiceViewController: UIViewController {
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    private let columnLayout = AddServiceFlowLayout(
        cellsPerRow: 3,
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.title = "Add Service"
        
        // Register cell classes
        self.collectionView!.delegate = self
        self.collectionView!.dataSource = self
        self.collectionView!.collectionViewLayout = columnLayout
        self.collectionView!.contentInsetAdjustmentBehavior = .always
        self.collectionView!.register(AddServiceViewCell.nib().self, forCellWithReuseIdentifier: AddServiceViewCell.identifier)

        
    }

// removed for brevity ....

// MARK: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout
extension AddServiceViewController : UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource {
    
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return allServices.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: AddServiceViewCell.identifier, for: indexPath) as! AddServiceViewCell
        cell.configure(with: allServices[indexPath.row])
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        self.onConfirmAdd(allServices[indexPath.row])
    }
}

Upvotes: 3

Views: 459

Answers (1)

Shehata Gamal
Shehata Gamal

Reputation: 100523

Here is a road map

1- You need to implement sizeForItemAt

2- Consider you have 3 columns per row , create a function that that accepts 3 Items ( introduced with the current indexPath.item ) from your model and manually calculate maximum height for each string in that model

3- Return the maximum height and by this you will set that height for all cells of same row

Upvotes: 0

Related Questions