For dynamic cell sizing based on the length of your content you basically have to do some approximations. So here is my solution. It involves a couple of trial and error to get the calculations correct. Please bear with me now!
override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
if let user = datasource?.item(indexPath) as? User {
let approximateWidthBioTextView = view.frame.width - 10 - 60 - 10
let size = CGSize(width: approximateWidthBioTextView, height: 1000)
let attributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15)]
let estimatedFrame = NSString(string: user.bio).boundingRect(with: size, options: .usesLineFragmentOrigin, attributes: attributes, context: nil)
return CGSize(width: view.frame.width, height: estimatedFrame.height + 73)
}
return CGSize(width: view.frame.width, height: 200)
}
Essentially you need to change the sizeForItem func and do some approximations. My content is determined by instances of a User Class. So make sure before you start to get reference to the content you have inside your cell. In my case, it's a users that needs to be displayed. In a few words, my solutions get's the cell's height based on user.bioText. Because this is the only element that changes size in my cell. So bear this in mind.
First, you must compute the collectionView with. In my case, it is frame's width minus margins (10, 60 and 10). Remember, it is important to make this right.
Second, in order to estimate the cell's height we need to wrap it in a rect that has some attributes that needs to be filled out. In the size constant we deliberately give a high value so the system takes what it really needs. Now for the attributes if your content is a string as in my case, make sure you have the same font size.
Last, in the return section return CGSize(width: view.frame.width, height: estimatedFrame.height + 73), we still have to make some corrections, because text has some internal padding if I can say so, and unless you correct that, your cell hight won't display right. Make sure you play with the last value estimatedFrame.height + 73, in my case 73, until you get to the point where your cells hight are dynamically allocated perfectly. That's the only way. It sure works for me.
BTW, don't bother if your code looks a bit different(not too different tho). I am using a framework, but the calculation principles are the same. If you are asking yourself, where does the 73 value comes form, well that's because on the top of the bio text I need to display in each cell, I have the User's Name and UserName's labels that have both a eight of 20, making 40 in total and 33 is the gap or margins between them. As long as you give it the minimum value, it should work. It doesn't really affect the result if you give it a too high value. So experiment!