I'm trying to use Compositional Layouts with UICollectionView where each cell contains an image with a dynamic aspect ratio. I'm using AutoLayout for the cells but I haven't been able to get the cells to auto-size correctly.
What's the right way to specify the cell's layout/constraints so that they have the correct height based on image aspect ratios?
In the code below, the images themselves get sized correctly, but their containing cells don't have the correct height (they just use their estimated height and don't adjust dynamically to the contained image). See screenshot.
(I know this is relatively straightforward using UICollectionViewFlowLayout, but I'm trying to learn compositional layouts and haven't been able to figure it out using this method)
Here's what I'm setting up in code:
For simplicity's sake of this post, I'm using a basic compositional layout with 1 group and 1 item using heightDimension: .estimated(300)
as placeholder
let layout = UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
let item = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(300)))
let group = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(300)), subitem: item, count: 1)
let section = NSCollectionLayoutSection(group: group)
return section
}
collectionView.setCollectionViewLayout(layout, animated: false)
Here's my code for the cell:
// during cellForItemAt, I call
// cell.setImage(url: [url from API], size: [dynamic value from API])
class ImageCell: UICollectionViewCell {
static let ImageCellIdentifier = String(describing: ImageCell.self)
var aspectRatioConstraint: NSLayoutConstraint?
private let imageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
func setImage(url: URL, size: CGSize) {
let aspectRatio = size.width / size.height
if let aspectRatioConstraint = aspectRatioConstraint {
imageView.removeConstraint(aspectRatioConstraint)
}
// set the image height constraint based on its aspect ratio
// anchor it to contentView.widthAnchor
aspectRatioConstraint = imageView.heightAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: aspectRatio)
NSLayoutConstraint.activate([
aspectRatioConstraint!,
])
imageView.sd_setImage(with: url)
contentView.layoutIfNeeded()
}
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
imageView.widthAnchor.constraint(equalTo: contentView.widthAnchor),
])
}
}
cell.setImage(url: [url from API], size: [dynamic value from API])
fromcellForItemAt
-- assuming that means you are pulling your url and size before layout begins, then you would want to use that data inside your compositional layout setup (instead of estimated heights).