I just ran into this problem on a UICollectionView and the way that i solved it similar to the answer above but in a pure UICollectionView way.
Create a custom UICollectionViewCell that contains whatever you will be filling it with to make it dynamic. I created its own .xib for it as it seems like the easiest approach.
Add constraints in that .xib that allow for the cell to be calculated from top to bottom. The re-sizing won't work if you haven't accounted for all of the height. Say you have a view on top, then a label underneath it, and another label underneath that. You would need to connect constraints to the top of the cell to the top of that view, then the bottom of the view to the top of the first label, bottom of first label to the top of the second label, and bottom of second label to bottom of cell.
Load the .xib into the viewcontroller and register it with the collectionView on viewDidLoad
let nib = UINib(nibName: CustomCellName, bundle: nil)
self.collectionView!.registerNib(nib, forCellWithReuseIdentifier: "customCellID")`
Load a second copy of that xib into the class and store it as a property so you can use it to determine the size of what that cell should be
let sizingNibNew = NSBundle.mainBundle().loadNibNamed(CustomCellName, owner: CustomCellName.self, options: nil) as NSArray
self.sizingNibNew = (sizingNibNew.objectAtIndex(0) as? CustomViewCell)!
Implement the UICollectionViewFlowLayoutDelegate
in your view controller. The method that matters is called sizeForItemAtIndexPath
. Inside that method you will need to pull the data from the datasource that is associated with that cell from the indexPath. Then configure the sizingCell and call preferredLayoutSizeFittingSize
. The method returns a CGSize which will consist of the width minus the content insets and the height that is returned from self.sizingCell.preferredLayoutSizeFittingSize(targetSize)
.
override func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
guard let data = datasourceArray?[indexPath.item] else {
return CGSizeZero
}
let sectionInset = self.collectionView?.collectionViewLayout.sectionInset
let widthToSubtract = sectionInset!.left + sectionInset!.right
let requiredWidth = collectionView.bounds.size.width
let targetSize = CGSize(width: requiredWidth, height: 0)
sizingNibNew.configureCell(data as! CustomCellData, delegate: self)
let adequateSize = self.sizingNibNew.preferredLayoutSizeFittingSize(targetSize)
return CGSize(width: (self.collectionView?.bounds.width)! - widthToSubtract, height: adequateSize.height)
}
In the class of the custom cell itself you will need to override awakeFromNib
and tell the contentView
that its size needs to be flexible
override func awakeFromNib() {
super.awakeFromNib()
self.contentView.autoresizingMask = [UIViewAutoresizing.FlexibleHeight]
}
In the custom cell override layoutSubviews
override func layoutSubviews() {
self.layoutIfNeeded()
}
In the class of the custom cell implement preferredLayoutSizeFittingSize
. This is where you will need to do any trickery on the items that are being laid out. If its a label you will need to tell it what its preferredMaxWidth should be.
func preferredLayoutSizeFittingSize(_ targetSize: CGSize)-> CGSize {
let originalFrame = self.frame
let originalPreferredMaxLayoutWidth = self.label.preferredMaxLayoutWidth
var frame = self.frame
frame.size = targetSize
self.frame = frame
self.setNeedsLayout()
self.layoutIfNeeded()
self.label.preferredMaxLayoutWidth = self.questionLabel.bounds.size.width
// calling this tells the cell to figure out a size for it based on the current items set
let computedSize = self.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)
let newSize = CGSize(width:targetSize.width, height:computedSize.height)
self.frame = originalFrame
self.questionLabel.preferredMaxLayoutWidth = originalPreferredMaxLayoutWidth
return newSize
}
All those steps should give you the correct sizes. If your getting 0 or other funky numbers than you haven't set up your constraints properly.