UIImage Sizing in Dynamic Collection View Cell with Autolayout and UICollectionViewCompositionalLayout

32 Views Asked by At

Context: I have a collection view with dynamic cell height defined by its cell's content. The cell's content: a UIView, a UILabel, and a UIImage (image and label vertically positioned).

Problem: Autolayout seems to work and size the cell accordingly when I have the image contentMode as scaleAspectFill. However, this crops image's sides. When I set the contentMode to scaleAspectFit it sizes the images correctly horizontally, but maintains the former height.

Attaching images of the results. Thanks for any tips and support!

set to scaleToFill

set to scaleToFit

Code for collection view layout:

func createLayout() -> UICollectionViewLayout {
    let _: CGFloat = (self.view.frame.width*1.33) + 50
    let estimatedHeight = CGFloat(100)
    let layoutSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                           heightDimension: .estimated(estimatedHeight))
    
    
    let item = NSCollectionLayoutItem(layoutSize: layoutSize)
    
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: layoutSize,
                                                   repeatingSubitem: item,
                                                   count: 1)
    let section = NSCollectionLayoutSection(group: group)
    section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
    section.interGroupSpacing = 10
    let layout = UICollectionViewCompositionalLayout(section: section)
    return layout
}

For Cell Registration:

    let newCell = UICollectionView.CellRegistration<CustomCell, Asset> {(cell, indexPath, item) in
        let image = UIImage(named: "test")
        cell.label.text = "No comments"
        cell.imageView.image = image
    }

For the custom cell:

class CustomGroupItemCell: UICollectionViewCell {

    let container = UIView()
    let imageView = UIImageView()
    let label = UILabel()
        
    override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }

    required init?(coder: NSCoder) {
       fatalError("not implemnted")
    } 

func configure() {
    container.translatesAutoresizingMaskIntoConstraints = false
    imageView.translatesAutoresizingMaskIntoConstraints = false
    label.translatesAutoresizingMaskIntoConstraints = false
    
    label.adjustsFontForContentSizeCategory = true
    label.numberOfLines = 0
    label.font = UIFont.systemFont(ofSize: 17, weight: .semibold)
    label.textColor = .label

    container.backgroundColor = .secondarySystemFill
    container.layer.cornerRadius = 12
    
    imageView.layer.cornerRadius = 5
    imageView.contentMode = .scaleAspectFill
    imageView.layer.masksToBounds = true

    contentView.addSubview(container)
    contentView.addSubview(label)
    contentView.addSubview(imageView)
    
    let views = [ "label": label, "container": container, "image": imageView]
    var constraints = [NSLayoutConstraint]()
    
    // Horizontal
    constraints.append(contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "H:|[container]|", options: [], metrics: nil, views: views))
    constraints.append(contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "H:|-20-[image]-20-|", options: [], metrics: nil, views: views))
    constraints.append(contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "H:|-20-[label]-20-|", options: [], metrics: nil, views: views))
  
    // Vertical
    constraints.append(contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "V:|[container]|",
        options: [], metrics: nil, views: views))
    constraints.append(contentsOf: NSLayoutConstraint.constraints(
        withVisualFormat: "V:|-20-[image]-20-[label]-24-|",
        options: [], metrics: nil, views: views))
    
    NSLayoutConstraint.activate(constraints)
}
}
1

There are 1 best solutions below

2
matt On BEST ANSWER

You cannot do this in any automagic way. When an image is ready to be placed in a cell, you need to read its size and adjust the image view's size constraints accordingly.

For example, in my app, the image view and cell width are fixed. When the image arrives, I work out its aspect ratio and set that value as the multiplier of the image view's aspect ratio constraint, thus setting the height to fit the image.