How to make dynamic height for UICollectionViewCell with dynamic type and fixed width?

644 Views Asked by At

I am working on a project where we use a UICollectionView with a custom cell. The cell looks like this. All of my elements are contained in a UIView. Of course, the text is much shorter. Anyway, I decide the width of the CollectionView in the sizeForItemAt function:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: collectionView.frame.width - 50, height: (collectionView.frame.width * 0.7))
}

I need to find a way to keep this fixed width but to make the height of the cell resizable when dynamic type is enabled. The labels at the bottom of the cell must be able to grow in size and therefore resize the cell at the bottom as much as they need without affecting the ImageView's size at the top. However, this seems harder than it looks since my ImageView has a size of 0.7 * the width of my CollectionView (so it has a proper height in any screen).

My labels are contained in a VerticalStackView and my VerticalStackView and the "1" label are contained in a HorizontalStackView. I have tried to use dynamic type on this kind of view hierarchy in a sample project and the view grows in height without affecting the ImageView but I cannot seem to do the same in a CollectionViewCell.

I tried using auto-layout with Automatic Size enabled for my CollectionViewCell but that just ruins both my width and height and also clips the labels that I cannot see them anymore.

I hope you can help me!

Thank you so much.

1

There are 1 best solutions below

0
On

You can find your height of the label using text as:

extension String {
    func height(withWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(
            with: constraintRect,
            options: .usesLineFragmentOrigin,
            attributes: [NSAttributedString.Key.font: font],
            context: nil)
        return ceil(boundingBox.height)
    }
}

Modify your collection view's sizeForItemAt as:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = collectionView.frame.width - 50.0
    let labelHeight = "your text".height(withWidth: width, font: .systemFont(ofSize: 17.0))
    let imageHeight = width * 0.7
    return CGSize(width: width, height: labelHeight+imageHeight)   
}