I have an infinite scroll in my UICollectionView and I've noticed that the way I estimate my cell height is the bottleneck of my collection view. It causes some long delays the more I scroll my collection view.
Is there a better way to estimate the heights of my cells?
The cells have different heights because I have a UILabel
in each of them. I assign a NSMutableAttributedStrings of different lengths to those UILabels:
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .justified
paragraphStyle.lineSpacing = 5.0
let attributedText = NSMutableAttributedString(string: " \(post.caption)", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15), .paragraphStyle: paragraphStyle, .baselineOffset: NSNumber(value: 0)])
attributedText.append(NSAttributedString(string: "\n\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 4)]))
let timeAgoDisplay = post.creationDate.timeAgoDisplay()
attributedText.append(NSAttributedString(string: timeAgoDisplay, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14), NSAttributedStringKey.foregroundColor: UIColor.storiesLightGray()]))
captionLabel.attributedText = attributedText
My sizeForItemAt
method. I've noticed that calling dummyCell.layoutIfNeeded()
makes my app very slow once I have more than 200 items in my collection view:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height: CGFloat = 180
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: height)
let dummyCell = HomePostCell(frame: frame)
dummyCell.post = presenter.posts[indexPath.item]
dummyCell.layoutIfNeeded()
let targetSize = CGSize(width: view.frame.width, height: 5000)
let estimatedSize = dummyCell.systemLayoutSizeFitting(targetSize)
let newHeight = max(height, estimatedSize.height)
return CGSize(width: view.frame.width, height: newHeight)
}
Thanks!
Ideally you should not use systemLayoutSizeFitting
in your sizeForItemAt
, because, as you say, it is slow.
You could precache some cell data, calculate the sizes, and store them in an array or similar, so that sizeForItemAt
merely has to do the lookup in the array — which is fast.
And you don't really need to use systemLayoutSizeFitting
, either; you can use your knowledge of the label size and contents to calculate the size (e.g. using NSAttributedString measurement methods).
That is what we used to do before systemLayoutSizeFitting
or auto layout existed, and it remains far faster.