This is my first time implementing UICollectionViewCompositionalLayout
and im confused on how to implement a Stretchy Header here. Right now I have just modified the dataSource.supplementaryViewProvider
function to include my custom HeaderView but I don't know how to make it attach to top.
I did find some code for other types of collectionView layouts but those don't work with UICollectionViewCompositionalLayout
. For other layouts I found that I need to override this override func layoutAttributesForElements(in rect: CGRect)
but where and how? I would like to know a method from the scratch. Below is my method which does not work at all with UICollectionViewCompositionalLayout
.
This is how im creating my Header:
class StretchyCollectionHeaderView: UICollectionReusableView {
static let reuseIdentifier = "stretchyCollectionHeaderView-reuse-identifier"
let imageView: UIImageView = {
let iv = UIImageView(image: UIImage(named: "HeaderHomePage"))
iv.contentMode = .scaleAspectFill
return iv
}()
override init(frame: CGRect) {
super.init(frame: frame)
// custom code for layout
backgroundColor = .red
addSubview(imageView)
imageView.fillSuperview()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Im using UICollectionReusableView
instead of UIView
because all section headers are passed as UICollectionReusableView
. Im using an extension which will connect imageView's bottom to header view and for other constraints, I did not include that because I think it isn't even used which I will come back to at the end of explanation.
This is my Layout for CollectionViewLayout with Stretchy Header:
class StretchyHeaderLayout: UICollectionViewCompositionalLayout {
// we want to modify the attributes of our header component somehow
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes = super.layoutAttributesForElements(in: rect)
layoutAttributes?.forEach({ (attributes) in
if attributes.representedElementKind == UICollectionView.elementKindSectionHeader && attributes.indexPath.section == 0 {
guard let collectionView = collectionView else { return }
let contentOffsetY = collectionView.contentOffset.y
print(contentOffsetY)
if contentOffsetY > 0 {
return
}
let width = collectionView.frame.width
let height = attributes.frame.height - contentOffsetY
// header
attributes.frame = CGRect(x: 0, y: contentOffsetY, width: width, height: height)
}
})
return layoutAttributes
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
return true
}
}
Then I simply register all cells and supplementary views. Then im setting up my collectionViewLayout like this:
func generateLayout() -> UICollectionViewLayout {
let layout = StretchyHeaderLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
let isWideView = layoutEnvironment.traitCollection.horizontalSizeClass == .regular
let sectionLayoutKind = Section.allCases[sectionIndex]
switch (sectionLayoutKind) {
case .locationTab: return self.generateLocationLayout(isWide: isWideView)
case .selectCategory: return self.generateCategoriesLayout()
case .valueAddedServices: return self.generatValueAddedServicesLayout(isWide: isWideView)
}
}
return layout
}
im setting layout = StretchyHeaderLayout
as this was the only possible way I could think of adding Stretchy Header layout.
And finally this is how im setting up my section headers:
dataSource.supplementaryViewProvider = { (
collectionView: UICollectionView,
kind: String,
indexPath: IndexPath) -> UICollectionReusableView? in
if indexPath.section == 0 {
guard let supplementaryView = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: StretchyCollectionHeaderView.reuseIdentifier,
for: indexPath) as? StretchyCollectionHeaderView else { fatalError("Cannot create header view") }
supplementaryView.imageView.image = UIImage(named: "HeaderHomePage")
return supplementaryView
}
else {
guard let supplementaryView = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: HeaderView.reuseIdentifier,
for: indexPath) as? HeaderView else { fatalError("Cannot create header view") }
supplementaryView.label.text = Section.allCases[indexPath.section].rawValue
return supplementaryView
}
}
Here im just using StretchyCollectionHeaderView
for first section and for others im using another HeaderView
which just contains a label.
What I think is happening is because of above function, its just setting header view with StreatchyCollectionHeaderView
for first section but not accessing any code inside StretchyHeaderLayout
and thus not sticking to top.
Unlike UITableView
, we cannot attach a CollectionView header instead of adding section header or in my case section header with image for first section and attach to top?
How to create a stretchy header for UICollectionViewCompositionalLayout
properly?