I need a label above every section in my Collection View. I've tried simply dragging a label into my header space above my prototype cell, but every time I run the app, the label is not visible.
Any help?
To add the custom label above every section in UICollectionView, please follow below steps
Connect the label in the section header to the UICollectionReusableView file
class SectionHeader: UICollectionReusableView {
@IBOutlet weak var sectionHeaderlabel: UILabel!
}
In the ViewController add the below code
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "SectionHeader", for: indexPath) as? SectionHeader{
sectionHeader.sectionHeaderlabel.text = "Section \(indexPath.section)"
return sectionHeader
}
return UICollectionReusableView()
}
Here "SectionHeader" is name of the file added to type UICollectionReusableView
Implement collectionView:viewForSupplementaryElementOfKind:atIndexPath: and supply a dequeued UICollectionElementKindSectionHeader containing your label. If this is a flow layout, be sure also to set the headerReferenceSize or you still won't see anything.
If you want a programmatic solution in Swift 4.2, you can do the following:
Setup the UICollectionViewDelegate and UICollectionViewDelegateFlowLayout
Make custom UICollectionReusableView subclasses with whatever views you want defined. Here's one for a header, you can create another for a footer with different characteristics:
class SectionHeader: UICollectionReusableView {
var label: UILabel = {
let label: UILabel = UILabel()
label.textColor = .white
label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
label.sizeToFit()
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
label.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 20).isActive = true
label.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Implement the viewForSupplementaryElementOfKind method with the custom views:
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
if kind == UICollectionView.elementKindSectionHeader {
let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! SectionHeader
sectionHeader.label.text = "TRENDING"
return sectionHeader
} else { //No footer in this case but can add option for that
return UICollectionReusableView()
}
}
Implement the referenceSizeForHeaderInSection and referenceSizeForFooterInSection methods. Header method shown below for example:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: collectionView.frame.width, height: 40)
}
I know this is an old question that predates compositional layouts (as introduced in WWDC 2019 Advances in Collection View Layout), but in case you are trying to do this with compositional layout and diffable data sources:
First, you would register your reuseIdentifier for your header view, like normal. E.g., if using a NIB:
collectionView.register(
UINib(nibName: "HeaderView", bundle: nil),
forSupplementaryViewOfKind: "header",
withReuseIdentifier: "HeaderView"
)
That presumes you have (a) a NIB for this supplementary view; and (b) a type with outlets for that view:
class HeaderView: UICollectionReusableView {
@IBOutlet weak var label: UILabel!
}
(Obviously, if you prefer programmatically creating your header view, then feel free to do that. You just want to make sure that you register that reuse identifier with your supplementary view.)
Then, when configuring your compositional layout, you would create a “boundary supplementary view” (i.e., a NSCollectionLayoutBoundarySupplementaryItem) and set your section’s boundarySupplementaryItems:
let itemSize = NSCollectionLayoutSize (…)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(…)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let headerSize = NSCollectionLayoutSize (
widthDimension: .fractionalWidth(1),
heightDimension: .estimated(44)
)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: "header",
alignment: .top
)
header.pinToVisibleBounds = true
section.boundarySupplementaryItems = [header]
let layout = UICollectionViewCompositionalLayout(section: section)
collectionView.collectionViewLayout = layout
Then, assuming you are using a diffable data source, you would make sure to configure the supplementaryViewProvider of your UICollectionViewDiffableDataSource:
supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in
guard let self else { return nil }
let text = sections[indexPath.section].id
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView
header.label.text = …
return header
}