Resize the cell of UICollectionView without the reload methods
I got the solution to my question with following line of code.
collectionView?.collectionViewLayout.invalidateLayout()
N.B. To animate this resizing, use UIView.animate
:
let duration: TimeInterval = 0.3
UIView.animate(withDuration: duration, animations: {
collectionView.collectionViewLayout.invalidateLayout()
})
I can confirm that Payal Maniyar's solution also works for UICollectionCompositional layouts as well. In my situation, I had a CollectionViewCell which only embedded a UITextView. Compositional layout correctly figured out the initial size of the text view in the cell. (Side note: you do this by disabling scrolling within the UITextView since it is already inside of another scrollable view). The problem was as the user typed directly into the text view, I wanted to change the height of the cell without reloading it. I wanted to avoid reloading the cell for 2 reasons. a) the reloaded cell flickers as the user types, b) the text view loses focus; thereby, forcing the user to click on the text view again to continue to type. So my solution is as follows:
I have a cell with only UITextView inside of it where the cell is also the UITextView's delegate as such:
class MyCollectionCell: UICollectionViewCell { @IBOutlet private weak var myTextView: UITextView! //this local variable used to determine if the intrinsic content size has changed or not private var textViewHeight: CGFloat = .zero ... ... override func awakeFromNib() { super.awakeFromNib() myTextView.delegate = self } ... ... }
Conform this cell to UITextViewDelegate
extension MyCollectionCell: UITextViewDelegate { func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { //this assumes that collection view already correctly laid out the cell //to the correct height for the contents of the UITextView //textViewHeight simply needs to catch up to it before user starts typing let fittingSize = textView.sizeThatFits(CGSize(width: myTextView.frame.width, height: CGFloat.greatestFiniteMagnitude)) textViewHeight = fittingSize.height return true } func textViewDidChange(_ textView: UITextView) { //flag to determine whether textview's size is changing var shouldResize = false //calculate fitting size after the content has changed let fittingSize = textView.sizeThatFits(CGSize(width: myTextView.frame.width, height: CGFloat.greatestFiniteMagnitude)) //if the current height is not equal to if textViewHeight != fittingSize.height { shouldResize = true //save the new height textViewHeight = fittingSize.height } //notify the cell's delegate (most likely a UIViewController) //that UITextView's intrinsic content size has changed //perhaps with a protocol such as this: delegate?.textViewDidChange(newText: textView.text, alsoRequiresResize: shouldResize) }
Then when the delegate receives the notification, perhaps saves the updated text and updates the layout such as this:
myCollectionView.collectionViewLayout.invalidateLayout()
The best part of this solution is that you do not have to invoke collectionView(_:layout:sizeForItemAt:) to resize the cell because you are not using the clunky FlowLayout to begin with.
Cheers!