I've built an NSCollectionView wrapper using NSViewRepresentable, but it refuses to scroll. The class looks something like this:
final class SwiftNSCollectionView: NSObject, NSViewRepresentable, NSCollectionViewDataSource // etc
{
// ... init ...
typealias NSViewType = NSCollectionView
func makeNSView(context: Context) -> NSCollectionView {
let collectionView = NSCollectionView()
scrollView.documentView = collectionView
updateNSView(collectionView, context: context)
return collectionView
}
func updateNSView(_ scrollView: NSCollectionView, context: Context) {
collectionView.dataSource = self
// ... other collectionView setup
}
// ...
}
Typically, NSCollectionView has a built-in NSScrollView.
I've tried:
No wrapper—the NSCollectionView simply doesn't scroll.
Wrapping this SwiftNSCollectionView in a SwiftUI ScrollView, but that causes two problems:
NSCollectionView collapses to 0 (which I can work around somewhat using a GeometryReader)NSCollectionView doesn't want to extend to the height of all its objects (which makes sense because it virtualizes them)Using NSCollectionViewCompositionalLayoutConfiguration with an orthogonal scroll direction (hacky):
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
// If the "official" scroll direction is horizontal,
// then the orthogonal direction becomes vertical,
// and we can scroll our one section 😈
let configuration = NSCollectionViewCompositionalLayoutConfiguration()
configuration.scrollDirection = .horizontal
But this seemed to mess up keyboarding: after a certain number of elements, arrow keys between elements either stopped working altogether or moved to the completely wrong elements. Seemed like a virtualization bug of some sort.
You can mitigate this by creating an NSScrollView manually.
NSViewType to be NSScrollView.NSCollectionView as the .documentView of the new scroll view.Then you can use your SwiftNSCollectionView directly in SwiftUI code and it will scroll properly without any custom work on your side.
final class SwiftNSCollectionView: NSObject, NSViewRepresentable, NSCollectionViewDataSource // etc {
// ... init ...
// No longer NSCollectionView
typealias NSViewType = NSScrollView
func makeNSView(context: Context) -> NSScrollView {
// Create an NSScrollView, too!
let scrollView = NSScrollView()
let collectionView = NSCollectionView()
scrollView.documentView = collectionView
updateNSView(scrollView, context: context)
return scrollView
}
func updateNSView(_ scrollView: NSScrollView, context: Context) {
// Since we get an NSScrollView, get the child!
let collectionView = scrollView.documentView as! NSCollectionView
collectionView.dataSource = self
// ... other collectionView setup
}
// ...
}