Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make UICollectionViewCells with UIImageView height based on image

I want to make a UICollectionView with the cells the width of the screen, but the height is based on the aspect ratio of the UIImageView inside.

In my current implementation all of the UICollectionViewCell instances are squares and this isn't what I want.

I want to use auto layout somehow to make the size of the image the width of the screen, but the relevant height depends on the aspect ratio of the image used.

Everything is done programatically in code.

enter image description here

My code:

class ViewController: UIViewController {
    var data = ["1","2","3","4","5","6","7","8","1","2","3","4","5","6","7","8"]

    var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.dataSource = self
        self.collectionView.delegate = self
        self.collectionView.register(SubclassedCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        self.collectionView.alwaysBounceVertical = true
        self.collectionView.backgroundColor = .white
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    override func loadView() {
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: UIScreen.main.bounds.size.width - 20, height: UIScreen.main.bounds.size.width / 1)
        layout.scrollDirection = .vertical
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        self.view = collectionView
    }
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        data.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? SubclassedCollectionViewCell {
            let data = self.data[indexPath.item]
            cell.setupCell(image: data)
            return cell
        }
        fatalError("Could not dequeue cell")
    }
}

with the cell

class SubclassedCollectionViewCell: UICollectionViewCell {
    var hotelImageView: UIImageView = {
        var hotelView = UIImageView()
        hotelView.contentMode = .scaleAspectFill
        hotelView.clipsToBounds = true
        return hotelView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(hotelImageView)
        hotelImageView.translatesAutoresizingMaskIntoConstraints = false
        hotelImageView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        hotelImageView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        hotelImageView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        hotelImageView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupCell(image: String) {
        if let image : UIImage = UIImage(named: image) {
            hotelImageView.image = image
        }
    }
}
like image 579
WishIHadThreeGuns Avatar asked May 06 '26 17:05

WishIHadThreeGuns


1 Answers

First, calculate the ratio based on the width and multiply by collection view width you can get a new height based on width.

Final Code: Remove itemSize from the loadview function. And calculate height and return item size in sizeForItemAt method.

loadView

override func loadView() {
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
//        layout.itemSize = CGSize(width: UIScreen.main.bounds.size.width - 20, height: UIScreen.main.bounds.size.width / 1) <-- Remove this
    layout.scrollDirection = .vertical
    collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
    self.view = collectionView
}

UICollectionViewDelegateFlowLayout

extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        data.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? SubclassedCollectionViewCell {
            let data = self.data[indexPath.item]
            cell.setupCell(image: data)
            return cell
        }
        fatalError("Could not dequeue cell")
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        guard let iamgeData = UIImage(named: self.data[indexPath.item]) else {
            return .zero
        }
        let width = collectionView.bounds.width
        let heightOnWidthRatio = iamgeData.size.height / iamgeData.size.width
        let height = width * heightOnWidthRatio
        
        return CGSize(width: width, height: height)
    }
}

enter image description here

like image 197
Raja Kishan Avatar answered May 09 '26 07:05

Raja Kishan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!