I'm trying to add headers and footers in a collectionView built as compositional.
Colmlection is built perfectly, but no supplementary func is never called: neither viewForSupplementaryElementOfKind nor referenceSizeForHeaderInSection or referenceSizeForFooterInSection
So I must miss some obvious declaration…
I've set the delegates, datasource, regitered classes, made room for headers and footers (did I did this correctly though ?).
Everything is for the time created in code, for easier testing.
import UIKit
class QuickCell4: UICollectionViewCell {
let containerView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureViews()
}
func configureViews() {
addSubview(containerView)
containerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
containerView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyHeaderFooterClass: UICollectionReusableView {
let titleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.purple
// Customize here
addSubview(titleLabel)
print("MyHeaderFooterClass")
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.sizeToFit()
titleLabel.frame.origin = CGPoint(x: 15, y: 10)
}
}
class FourthViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
var collectionView: UICollectionView!
let dataColors = [
[UIColor.red, UIColor.blue, UIColor.green, UIColor.magenta, UIColor.purple, UIColor.orange, UIColor.black, UIColor.lightGray, UIColor.blue],
[UIColor.red, UIColor.blue, UIColor.green, UIColor.magenta, UIColor.blue]
]
let collectionViewHeaderFooterReuseIdentifier = "MyHeaderFooterClass"
func createCustomLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (section: Int, environment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let leadingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: NSCollectionLayoutDimension.fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)))
leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let leadingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(1))
let leadingGroup = NSCollectionLayoutGroup.vertical(layoutSize: leadingGroupSize, subitem: leadingItem, count: 1)
let trailingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(widthDimension: NSCollectionLayoutDimension.fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)))
trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
let trailingGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.3), heightDimension: .fractionalHeight(1))
let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: trailingGroupSize, subitem: trailingItem, count: 2)
let containerGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(250))
let containerGroup = NSCollectionLayoutGroup.horizontal(layoutSize: containerGroupSize, subitems: [leadingGroup, trailingGroup])
let section = NSCollectionLayoutSection(group: containerGroup)
section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 0, bottom: 20, trailing: 0)
// section.supplementariesFollowContentInsets = true
return section
}
return layout
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionView = UICollectionView(frame: .zero, collectionViewLayout: createCustomLayout())
collectionView.backgroundColor = .systemTeal // .white
self.collectionView.delegate = self
self.collectionView.dataSource = self
self.collectionView.register(QuickCell4.self, forCellWithReuseIdentifier: "cellID4")
self.collectionView.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)
self.collectionView.register(MyHeaderFooterClass.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier)
// tried adding the following, no change
// if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
// flowLayout.sectionFootersPinToVisibleBounds = true
// }
configureCollectionView()
}
func configureCollectionView() {
collectionView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(collectionView)
NSLayoutConstraint.activate([
self.collectionView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor),
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
self.collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor)
])
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
print("Sections: ", dataColors.count)
return dataColors.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataColors[section].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellID4", for: indexPath) as? QuickCell4 {
let colorArray = dataColors[indexPath.section]
cell.containerView.backgroundColor = colorArray[indexPath.row]
return cell
} else {
return UICollectionViewCell()
}
}
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
print("UICollectionViewDelegateFlowLayout")
switch kind {
case UICollectionView.elementKindSectionHeader:
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier, for: indexPath) as! MyHeaderFooterClass
headerView.backgroundColor = UIColor.blue
headerView.titleLabel.text = "Header"
return headerView
case UICollectionView.elementKindSectionFooter:
let footerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: collectionViewHeaderFooterReuseIdentifier, for: indexPath) as! MyHeaderFooterClass
footerView.backgroundColor = UIColor.green
footerView.titleLabel.text = "Footer"
return footerView
default:
assert(false, "Unexpected element kind")
}
}
// Step 6: Handle size / make it appear:
// Does it needs to be declared @objc
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
print("referenceSizeForHeaderInSection")
return CGSize(width: collectionView.frame.width, height: 30.0)
}
// Does it needs to be declared @objc
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
print("referenceSizeForFooterInSection")
return CGSize(width: 60.0, height: 30.0)
}
func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let layoutAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: indexPath)
if elementKind == UICollectionView.elementKindSectionHeader {
layoutAttributes.frame = CGRect(x: 0.0, y: 0.0, width: 200, height: 50) // contentWidth, height: headerHeight)
// layoutAttributes.zIndex = Int.max - 3
}
return layoutAttributes
}
}
Credit: h ttps://hackernoon.com/complex-collection-view-layouts-in-swift-with-compositional-layout-z0bmk35kw
The only log I get is:
Sections: 2
Sections: 2
Found what was missing.
In
func createCustomLayout() -> UICollectionViewLayout {
I had to add the definition of header and footer (could also add a left or right "leader" and "trailer"
let footerHeaderSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(50.0))
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: footerHeaderSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top)
let footer = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: footerHeaderSize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom)
section.boundarySupplementaryItems = [header, footer]
This was really helful: h ttps://medium.com/flawless-app-stories/all-what-you-need-to-know-about-uicollectionviewcompositionallayout-f3b2f590bdbe