Code Block | import SwiftUI |
| import AppKit |
|
| final class HostingCellView: NSCollectionViewItem { |
| func setView<Content>(_ newValue: Content) where Content: View { |
| for view in self.view.subviews { |
| view.removeFromSuperview() |
| } |
| let view = NSHostingView(rootView: newValue) |
| view.autoresizingMask = [.width, .height] |
| self.view.addSubview(view) |
| } |
| } |
|
| struct Cell: View { |
| static let reuseIdentifier = NSUserInterfaceItemIdentifier("Cell") |
|
| var body: some View { |
| Text("Cell") |
| } |
| } |
|
| final class HostingSupplementaryView: NSView, NSCollectionViewElement { |
| override init(frame frameRect: NSRect) { |
| super.init(frame: frameRect) |
| } |
|
| @objc required dynamic init?(coder aDecoder: NSCoder) { |
| fatalError("init(coder:) has not been implemented") |
| } |
|
| func setView<Content>(_ newValue: Content) where Content: View { |
| for view in self.subviews { |
| view.removeFromSuperview() |
| } |
| let view = NSHostingView(rootView: newValue) |
| view.autoresizingMask = [.width, .height] |
| self.addSubview(view) |
| } |
| } |
|
| struct Header: View { |
| static let reuseIdentifier = NSUserInterfaceItemIdentifier("Header") |
|
| var body: some View { |
| Text("Header") |
| } |
| } |
|
| struct Footer: View { |
| static let reuseIdentifier = NSUserInterfaceItemIdentifier("Footer") |
|
| var body: some View { |
| Text("Footer") |
| } |
| } |
|
| struct Collection: NSViewRepresentable { |
| static let headerIdentifier = "ViewHeader" |
| static let footerIdentifier = "ViewFooter" |
|
| typealias NSViewType = NSCollectionView |
|
| func makeNSView(context: Context) -> NSCollectionView { |
| let view = NSCollectionView() |
| view.delegate = context.coordinator |
|
| view.register(HostingCellView.self, forItemWithIdentifier: Cell.reuseIdentifier) |
| view.register(HostingSupplementaryView.self, forSupplementaryViewOfKind: Self.headerIdentifier, withIdentifier: Header.reuseIdentifier) |
| view.register(HostingSupplementaryView.self, forSupplementaryViewOfKind: Self.footerIdentifier, withIdentifier: Footer.reuseIdentifier) |
|
| view.collectionViewLayout = NSCollectionViewCompositionalLayout { (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection in |
|
| let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0)) |
| let item = NSCollectionLayoutItem(layoutSize: itemSize) |
| let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitem: item, count: 1) |
|
| let section = NSCollectionLayoutSection(group: group) |
| section.orthogonalScrollingBehavior = .continuous |
|
| let supplementarySize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44)) |
| let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: supplementarySize, elementKind: Self.headerIdentifier, alignment: .topLeading) |
| let footer = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: supplementarySize, elementKind: Self.footerIdentifier, alignment: .bottomTrailing) |
| section.boundarySupplementaryItems = [header, footer] |
|
| return section |
| } |
|
| let dataSource = NSCollectionViewDiffableDataSource<Int, Int>(collectionView: view) { (view, indexPath, sectionIndex) -> NSCollectionViewItem? in |
| guard let item = view.makeItem(withIdentifier: Cell.reuseIdentifier, for: indexPath) as? HostingCellView else { |
| fatalError() |
| } |
| item.setView(Cell()) |
| return item |
| } |
| dataSource.supplementaryViewProvider = { (view: NSCollectionView, kind: String, indexPath: IndexPath) -> (NSView & NSCollectionViewElement)? in |
| switch kind { |
| case Self.headerIdentifier: |
| guard let supplementary = view.makeSupplementaryView(ofKind: kind, withIdentifier: Header.reuseIdentifier, for: indexPath) as? HostingSupplementaryView else { |
| fatalError() |
| } |
| supplementary.setView(Header()) |
| return supplementary |
| case Self.footerIdentifier: |
| guard let supplementary = view.makeSupplementaryView(ofKind: kind, withIdentifier: Footer.reuseIdentifier, for: indexPath) as? HostingSupplementaryView else { |
| fatalError() |
| } |
| supplementary.setView(Footer()) |
| return supplementary |
| default: |
| return nil |
| } |
| } |
|
| context.coordinator.dataSource = dataSource |
|
| return view |
| } |
|
| func updateNSView(_ nsView: NSCollectionView, context: Context) { |
| var snapshot = NSDiffableDataSourceSnapshot<Int, Int>() |
|
| snapshot.appendSections([0]) |
| // snapshot.appendItems(Array<Int>(0..<10), toSection: 0) |
|
| context.coordinator.dataSource?.apply(snapshot, animatingDifferences: true) |
| } |
|
| func makeCoordinator() -> Coordinator { |
| return Coordinator(self) |
| } |
|
| class Coordinator: NSObject { |
| let view: Collection |
| var dataSource: NSCollectionViewDiffableDataSource<Int, Int>? = nil |
|
| init(_ view: Collection) { |
| self.view = view |
| } |
| } |
| } |
|
| extension Collection.Coordinator: NSCollectionViewDelegate {} |