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
}
// ...
}