如何滚动以在 SwiftUI 中托管的 NSCollectionView 工作?
How can I get scrolling to work for an NSCollectionView hosted in SwiftUI?
我使用 NSViewRepresentable
构建了一个 NSCollectionView
包装器,但它拒绝滚动。 class 看起来像这样:
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
}
// ...
}
通常,NSCollectionView
有一个内置的 NSScrollView。
我试过了:
没有包装器——NSCollectionView
根本不滚动。
将此 SwiftNSCollectionView
包装在 SwiftUI ScrollView
中,但这会导致两个问题:
NSCollectionView
的高度折叠为 0(我可以使用 GeometryReader
解决这个问题)
NSCollectionView
不想扩展到所有对象的高度(这是有道理的,因为它虚拟化了它们)
使用正交滚动方向的 NSCollectionViewCompositionalLayoutConfiguration
(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
但这似乎搞乱了键盘操作:在一定数量的元素之后,元素之间的箭头键要么完全停止工作,要么移到完全错误的元素上。似乎是某种虚拟化错误。
您可以通过手动创建 NSScrollView
来缓解这种情况。
- 将
NSViewType
更新为 NSScrollView
。
- 根据需要更新函数签名。
- 使用您现有的
NSCollectionView
作为新滚动视图的 .documentView
。
然后您可以直接在 SwiftUI 代码中使用您的 SwiftNSCollectionView
,它会正确滚动,而无需您进行任何自定义工作。
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
}
// ...
}
我使用 NSViewRepresentable
构建了一个 NSCollectionView
包装器,但它拒绝滚动。 class 看起来像这样:
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
}
// ...
}
通常,NSCollectionView
有一个内置的 NSScrollView。
我试过了:
没有包装器——
NSCollectionView
根本不滚动。将此
SwiftNSCollectionView
包装在 SwiftUIScrollView
中,但这会导致两个问题:NSCollectionView
的高度折叠为 0(我可以使用GeometryReader
解决这个问题)NSCollectionView
不想扩展到所有对象的高度(这是有道理的,因为它虚拟化了它们)
使用正交滚动方向的
NSCollectionViewCompositionalLayoutConfiguration
(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
但这似乎搞乱了键盘操作:在一定数量的元素之后,元素之间的箭头键要么完全停止工作,要么移到完全错误的元素上。似乎是某种虚拟化错误。
您可以通过手动创建 NSScrollView
来缓解这种情况。
- 将
NSViewType
更新为NSScrollView
。 - 根据需要更新函数签名。
- 使用您现有的
NSCollectionView
作为新滚动视图的.documentView
。
然后您可以直接在 SwiftUI 代码中使用您的 SwiftNSCollectionView
,它会正确滚动,而无需您进行任何自定义工作。
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
}
// ...
}