I've been driven insane for hours as I can't get around with the issue.
I have a collection view which can have different section with different no. of items in each. For each section I need to use a section header of different type. So for this, I'm going to use UICollectionReusableView
. But I can't seem to succeed in using a custom subclass of UICollectionReusableView
by means of UINib
registration.
A crash happens when I downcast the reusable view to my subclass. Like:
let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
withReuseIdentifier: "FriendHeaderView",
for: indexPath) as! FriendHeaderView
Below is the code snippet:
class ViewController: UIViewController {
@IBOutlet weak var collectionView: UICollectionView!
private let viewModel = ProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self
collectionView.delegate = self
// more code
collectionView.register(UINib(nibName: "FriendHeaderView", bundle: nil),
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: "FriendHeaderView")
}
}
Now here is the data source implementation:
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// valid implementation
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let friendHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FriendHeaderView", for: indexPath) as! FriendHeaderView
// *** Crash happens here *** //
return friendHeader
default:
assert(false, "Invalid element type")
}
}
}
And I don't know why the collectionView(_:layout:referenceSizeForHeaderInSection:)
needs to be also implemented. So here it is:
extension ViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
let size = CGSize(width: collectionView.bounds.width, height: 100)
return size
}
}
Okay, now come to the point: The above mentioned crash doesn't happen at all if I don't downcast with
as!
operator. Well, if I use section header from the storyboard instead ofUINib
registration, there is no crash.
If I'm going to need multiple type header, then I can't also use storyboard approach or without down-casting approach as I need to feed data to those headers.
What can I do to have multiple type headers with view built from interface builder?
I've made a demo project with what I've said above. If anyone is interested please check out that.
Once you assign proper class and identifier in your Xib file, then it will work without crashes.