So this is an interesting issue we found with UICollectionViewFlowLayout
on iOS 10 (still an issue on 11) and using UICollectionViewFlowLayoutAutomaticSize
for the estimatedItemSize.
We've found that using UICollectionViewFlowLayoutAutomaticSize
for the estimatedItemSize results in the footer supplementary view floating above the bottom few cells.(Red/Pink is the header, Green is the footer.)
Here is the VC code of a sample app:
import UIKit
class ViewController: UIViewController {
// MARK: Properties
let texts: [String] = [
"This is some text",
"This is some more text",
"This is even more text"
]
// MARK: Outlets
@IBOutlet var collectionView: UICollectionView! {
didSet {
self.collectionView.backgroundColor = .orange
}
}
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Layout
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
if #available(iOS 10.0, *) {
layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
} else {
layout.estimatedItemSize = CGSize(width: self.collectionView.bounds.width, height: 50)
}
self.collectionView.collectionViewLayout = layout
// Register Cells
self.collectionView.register(UINib(nibName: "TextCell", bundle: nil), forCellWithReuseIdentifier: String(describing: TextCell.self))
self.collectionView.register(UINib(nibName: "SectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: String(describing: SectionHeader.self))
self.collectionView.register(UINib(nibName: "SectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionFooter, withReuseIdentifier: String(describing: SectionFooter.self))
self.collectionView.reloadData()
}
}
// MARK: - UICollectionViewDelegateFlowLayout Methods
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: self.collectionView.bounds.width, height: 90)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
return CGSize(width: self.collectionView.bounds.width, height: 90)
}
}
// MARK: - UICollectionViewDataSource Methods
extension ViewController: UICollectionViewDataSource {
public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.texts.count
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: TextCell.self), for: indexPath)
if let textCell = cell as? TextCell {
let text = self.texts[indexPath.row]
textCell.configure(text: text)
}
return cell
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionElementKindSectionHeader:
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionHeader.self), for: indexPath)
case UICollectionElementKindSectionFooter:
return collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: String(describing: SectionFooter.self), for: indexPath)
default:
return UICollectionReusableView()
}
}
}
// MARK: - UICollectionViewDelegate Methods
extension ViewController: UICollectionViewDelegate {
}
Has anyone managed to get UICollectionViewFlowLayoutAutomaticSize to work on iOS 10 with supplementary header and footer views? If I add a size to the estimatedItemSize then it appears to work, but I want to know if there is a bug in using the new iOS 10 feature or if I'm using it incorrectly.
The bug filed with Apple has the ID: 28843116
UPDATE: This still appears to be an issue on 10.3 Beta 1
UPDATE 2: This still appears to be an issue in iOS 11 Beta 1
UICollectionViewFlowLayout
supports auto layout for cells very well, BUT it does not supports it for supplementary views. Each time when auto layout code updates cell's frame it does nothing with headers and footers. So you need to tell layout that it should invalidate headers and footers. And there is a method for this!
You should override UICollectionViewLayout
's func invalidationContext(forPreferredLayoutAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes: UICollectionViewLayoutAttributes)
and perform supplementary view invalidation in it.
Example:
override open func invalidationContext(forPreferredLayoutAttributes preferred: UICollectionViewLayoutAttributes,
withOriginalAttributes original: UICollectionViewLayoutAttributes)
-> UICollectionViewLayoutInvalidationContext {
let context: UICollectionViewLayoutInvalidationContext = super.invalidationContext(
forPreferredLayoutAttributes: preferred,
withOriginalAttributes: original
)
let indexPath = preferred.indexPath
if indexPath.item == 0 {
context.invalidateSupplementaryElements(ofKind: UICollectionElementKindSectionHeader, at: [indexPath])
}
return context
}
In example above I am using invalidation context provided by UICollectionViewFlowLayout
to invalidate header supplementary view if first cell in section was invalidated. You can use this method for footer invalidation as well.