Pavel Gatilov Pavel Gatilov - 1 year ago 133
iOS Question

UICollectionView with autosizing cell (estimatedSize) and sectionHeadersPinToVisibleBounds goes mental

Consider the following situation. I have an

UICollectionView
(inside
UICollectionViewController
), which looks almost the same as
UITableView
(the reason why I don't use
UITalbeView
is because I have non data views on layout, that I don't want to manage and mess with my
IndexPath
).
In order to achieve the autosizing cells I've set
estimatedItemSize
, something like that:

layout.estimatedItemSize = CGSize(width: self.view.bounds.size.width, height: 72)


Also, in my cell I have layout attributes:

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
return layoutAttributes
}


So, by doing that I've got exact layout as
UITableView
with autosizing. And it works perfectly.

Now, I am trying to add the header and pin it on scrolling to the top of the section, like that:

layout.sectionHeadersPinToVisibleBounds = false


but layout goes into weird state, I have glitches all over the place, cells overlapping each other, and headers sometimes doesn't stick.

UPDATE:

The code of view controller and cell:

class ViewController: UICollectionViewController {

override func viewDidLoad() {
super.viewDidLoad()
let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionHeadersPinToVisibleBounds = true
layout.estimatedItemSize = CGSize(width: collectionView?.bounds.size.width ?? 0, height: 36) // enables dynamic height
}

override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 10
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.heightConstraint.constant = CGFloat(indexPath.row * 10 % 100) + 10 // Random constraint to make dynamic height work

return cell
}

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
return collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath)
}

class CustomCell : UICollectionViewCell {
let identifier = "CustomCell"

@IBOutlet weak var rectangle: UIView!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!

override func awakeFromNib() {
translatesAutoresizingMaskIntoConstraints = false
}

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
return layoutAttributes
}


Animation of result

Details of lagging in video: https://vimeo.com/203284395

Answer Source

Update from WWDC 2017:

My colleague was on WWDC 2017, and he asked one of the UIKit engineers about this issue. The engineer confirmed that this issue is known bug by Apple and there is no fix at that moment.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download