Reputation: 4402
How can I resize the cell size of UICollectionView
without reload
methods?
Just want to call following method on some event. I do not want to reload UICollectionView
or any of its section or row.
func collectionView(collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
Upvotes: 13
Views: 12074
Reputation: 3219
I'm showing the video inside UICollectionViewCell
. I've seen that UIView.animate
doesn't work correctly for updating the height with fade animation. The below code works perfectly.
collectionView.performBatchUpdates({
collectionView.collectionViewLayout.invalidateLayout()
})
Upvotes: 1
Reputation: 4402
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()
})
Upvotes: 56
Reputation: 975
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!
Upvotes: 1