How to add section header to NSCollectionView in macOS

Issue #437

Normal

Use Omnia for itemId extension

HeaderCell.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final class HeaderCell: NSView, NSCollectionViewSectionHeaderView {
let label: NSTextField = withObject(NSTextField(labelWithString: "")) {
$0.textColor = R.color.header
$0.font = R.font.header
$0.alignment = .left
$0.lineBreakMode = .byTruncatingTail
}

override init(frame frameRect: NSRect) {
super.init(frame: frameRect)

addSubviews([label])

activate(
label.anchor.centerY,
label.anchor.left.constant(8)
)
}

required init?(coder: NSCoder) {
fatalError()
}
}

ViewController.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
collectionView.register(
HeaderCell.self,
forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader,
withIdentifier: HeaderCell.itemId
)

func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {

if kind == NSCollectionView.elementKindSectionHeader {
let view = collectionView.makeSupplementaryView(
ofKind: kind,
withIdentifier: HeaderCell.itemId,
for: indexPath
) as! HeaderCell

let menu = app.menus[indexPath.section]
view.label.stringValue = menu.name

return view
} else {
return NSView()
}
}

In generic subclass

If use CollectionViewHandler from Omnia, then need to add @objc due to a bug in Swift compiler for subclassing generic class

1
@objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:)

MyHandler.swift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import AppKit
import Omnia

class MyHandler: CollectionViewHandler<App.Key, KeyCell> {
override init() {
super.init()

layout.headerReferenceSize = NSSize(width: 300, height: 30)

collectionView.register(
HeaderCell.self,
forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader,
withIdentifier: HeaderCell.itemId
)
}

@objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:)
func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {

if kind == NSCollectionView.elementKindSectionHeader {
let view = collectionView.makeSupplementaryView(
ofKind: kind,
withIdentifier: HeaderCell.itemId,
for: indexPath
) as! HeaderCell

let menu = app.menus[indexPath.section]
view.label.stringValue = menu.name

return view
} else {
return NSView()
}
}
}

Use Omnia

Use CollectionViewSectionHandler from Omnia

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class SectionHandler: CollectionViewSectionHandler<App.Key, KeyCell, HeaderCell> {

}

sectionHandler.configureHeader = { section, view in
view.label.stringValue = section.name
}

sectionHandler.configure = { item, cell in
cell.shortcutLabel.stringValue = item.shortcut
cell.nameLabel.stringValue = item.name
}

sectionHandler.itemSize = { [weak self] in
guard let self = self else {
return .zero
}

let width = self.sectionHandler.collectionView.frame.size.width
- self.sectionHandler.layout.sectionInset.left
- self.sectionHandler.layout.sectionInset.right

return CGSize(width: width, height: 18)
}

Read more

Comments