Swift 在NSCollectionViewDiffableDataSource中附加节
我正在尝试使用Swift 在NSCollectionViewDiffableDataSource中附加节,swift,macos,cocoa,nscollectionview,Swift,Macos,Cocoa,Nscollectionview,我正在尝试使用NSCollectionViewDiffableDataSource和nsCollectionViewCompositionLayout创建一个具有动态节数的NSCollectionView 集合视图用于显示搜索结果,节的数量取决于找到的结果的类型和数量。每个部分使用不同的布局显示其内容类型 数据源声明为NSCollectionViewDiffableDataSource,其中SearchResult是一个使用UUID()实现Hashable的类。结果为零的节不是空的,而是不存在于
NSCollectionViewDiffableDataSource
和nsCollectionViewCompositionLayout
创建一个具有动态节数的NSCollectionView
集合视图用于显示搜索结果,节的数量取决于找到的结果的类型和数量。每个部分使用不同的布局显示其内容类型
数据源声明为NSCollectionViewDiffableDataSource
,其中SearchResult
是一个使用UUID()
实现Hashable
的类。结果为零的节不是空的,而是不存在于集合视图中
显示“我的视图控制器”时,我将清除现有搜索结果:
func清除搜索结果(动画:Bool){
let snapshot=NSDiffableDataSourceSnapshot()
应用(快照、动画差异:动画)
}
执行搜索时,我尝试为找到的每种类型的结果向集合视图添加一个部分:
//执行搜索的代码
var snapshot=NSDiffableDataSourceSnapshot()
//如果我附加了多个节,则在apply()中会引发异常:
//snapshot.AppendSection([ViewController.trackSection,ViewController.albumSection])
snapshot.AppendSection([ViewController.trackSection])
snapshot.appendItems(轨迹,toSection:ViewController.trackSection)
//这也会导致apply()中出现异常:
//snapshot.AppendSection([ViewController.albumSection])
//snapshot.appendItems(相册,toSection:ViewController.albumSection)
dataSource.apply(快照、动画差异:true)
堆栈跟踪是:
2019-11-10 13:34:29.883728-0600 DiffableTest[64931:1820050] [General] An uncaught exception was raised
2019-11-10 13:34:29.883813-0600 DiffableTest[64931:1820050] [General] -[NSCollectionView insertSections:] Section index 1 out of bounds
2019-11-10 13:34:29.883937-0600 DiffableTest[64931:1820050] [General] (
0 CoreFoundation 0x00007fff33a98f53 __exceptionPreprocess + 250
1 libobjc.A.dylib 0x00007fff69b5e835 objc_exception_throw + 48
2 CoreFoundation 0x00007fff33a98da9 +[NSException raise:format:] + 189
3 UIFoundation 0x00007fff6469fd5b -[_NSCollectionViewCore insertSections:] + 267
4 UIFoundation 0x00007fff6465dd5e -[_NSDiffableDataSourceViewUpdater _performNSCollectionViewInsertUpdate:] + 222
5 UIFoundation 0x00007fff6465db33 -[_NSDiffableDataSourceViewUpdater _performViewUpdates:] + 594
6 AppKit 0x00007fff3156d8e8 __58-[NSCollectionView performBatchUpdates:completionHandler:]_block_invoke + 21
7 UIFoundation 0x00007fff646aabe5 -[_NSCollectionViewCore _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:animator:] + 323
8 UIFoundation 0x00007fff646aaa7f -[_NSCollectionViewCore _performBatchUpdates:completion:invalidationContext:tentativelyForReordering:] + 90
9 UIFoundation 0x00007fff646aaa02 -[_NSCollectionViewCore _performBatchUpdates:completion:invalidationContext:] + 74
10 UIFoundation 0x00007fff646aa957 -[_NSCollectionViewCore performBatchUpdates:completion:] + 53
11 AppKit 0x00007fff3156d7f4 -[NSCollectionView performBatchUpdates:completionHandler:] + 282
12 UIFoundation 0x00007fff6465d2ee -[_NSDiffableDataSourceViewUpdater _performUpdateWithCollectionViewUpdateItems:dataSourceSnapshotter:updateHandler:completion:] + 528
13 UIFoundation 0x00007fff646c72a2 -[__NSDiffableDataSource _commitNewDataSource:withViewUpdates:completion:] + 265
14 UIFoundation 0x00007fff646c1cb9 __66-[__NSDiffableDataSource applyDifferencesFromSnapshot:completion:]_block_invoke.259 + 190
15 UIFoundation 0x00007fff646c1fd2 __66-[__NSDiffableDataSource applyDifferencesFromSnapshot:completion:]_block_invoke.284 + 170
16 libdispatch.dylib 0x000000010039e78f _dispatch_client_callout + 8
17 libdispatch.dylib 0x00000001003af4cb _dispatch_lane_barrier_sync_invoke_and_complete + 135
18 UIFoundation 0x00007fff646c172d -[__NSDiffableDataSource applyDifferencesFromSnapshot:completion:] + 842
19 UIFoundation 0x00007fff64765417 +[_NSUIAnimator performWithAnimation:] + 90
20 UIFoundation 0x00007fff646c2939 -[__NSDiffableDataSource applyDifferencesFromSnapshot:animatingDifferences:completion:] + 158
21 libswiftAppKit.dylib 0x00007fff6a2f6bb3 $s6AppKit34NSCollectionViewDiffableDataSourceC5apply_20animatingDifferences10completionyAA010NSDiffablefG8SnapshotVyxq_G_SbyycSgtF + 211
22 DiffableTest 0x0000000100005c73 $s12DiffableTest14ViewControllerC13performSearchyyyXlSgF + 3059
我是否以某种方式误用了API
有关示例项目,请参见。对我的部分进行一些简短测试后,当数据源使用动画应用快照时,似乎会引发节索引越界异常。如果在修改任何部分时禁用动画,则不会引发异常。(至少在我的基本测试中没有。) 以下代码似乎对我正常工作:
let outgoingSectionCount = dataSource.numberOfSections(in:collectionView)
let incomingSectionCount = snapshot.sectionIdentifiers.count
let shouldAnimate = true
let canAnimate = outgoingSectionCount == incomingSectionCount
dataSource.apply(snapshot, animatingDifferences:(shouldAnimate && canAnimate)
YMMV
跟进
正如您在上面的评论中所回避的那样,Apple DTS建议您在附加部分之后但在附加任何项目之前调用dataSource.apply(…)
。成功插入节后,您应该能够插入项并再次调用dataSource.apply(…)
我发现有时需要调用两次apply(\uquo:animatingDifferences:)
,一次是在调用appendSection(\uquo)
后调用animationfalse
,一次是在调用appendItems(\uquo:)
后调用
例如:
extension AnimalsViewController {
enum Section {
case main
}
private func makeDataSource() -> UITableViewDiffableDataSource<Section, String> {
UITableViewDiffableDataSource(tableView: tableView) { tableView, indexPath, name in
let cell = tableView
.dequeueReusableCell(withIdentifier: CellIdentifier.animalCell.rawValue,
for: indexPath)
cell.textLabel?.text = name
return cell
}
}
private func update() {
activityIndicator.stopAnimating()
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
snapshot.appendSections([.main])
dataSource.apply(snapshot, animatingDifferences: false)
snapshot.appendItems(viewModel.animalNames)
dataSource.apply(snapshot, animatingDifferences: true)
}
}
扩展AnimalsViewController{
枚举部分{
主箱
}
private func makeDataSource()->UITableViewDiffableDataSource{
UITableViewDiffableDataSource(tableView:tableView){tableView,indexPath,中的名称
让单元格=表格视图
.dequeueReusableCell(标识符为CellIdentifier.animalCell.rawValue,
for:indexath)
cell.textlab?.text=名称
返回单元
}
}
私有函数更新(){
activityIndicator.stopAnimating()
var snapshot=NSDiffableDataSourceSnapshot()
snapshot.appendSections([.main])
dataSource.apply(快照、动画差异:false)
snapshot.appendItems(viewModel.animalNames)
dataSource.apply(快照、动画差异:true)
}
}
似乎与
NSCollectionViewDiffableDataSource
相关的一些bug已经解决了macOS 11。在macOS 10.15中崩溃的动画中应用快照。*,在大多数情况下在macOS 11中似乎都有效。我自己也遇到了这个令人烦恼的问题。唯一可靠的修复方法是在应用快照之前使布局无效
if snapshot.sectionIdentifiers.isEmpty {
collectionView.collectionViewLayout.invalidateLayout()
}
dataSource.apply(snapshot, ...)
你有没有想过?您是使用纯Swift结构/类作为标识符,还是像示例代码中那样使用
NSObject
派生类?在macOS上使用纯Swift实体存在一些混乱,因为NSCollectionViewDiffableDataSource
仅在10.15.1中引入(没有任何文档或发行说明)。使用基于引用的API对我来说是可行的,但我在10.15.3下使用纯Swift API时也会遇到异常情况。@kennyc我从来没有让事情像我想要的/预期的那样工作。我用的是纯Swift。从我收集的NSCollectionView
中发现了附加节和移动项的错误。基本上在每次操作(步骤更新)之后调用apply()
,并且始终至少有一个部分,就可以让事情顺利进行。感谢您的后续关注。我(再次)收到了一份关于DTS的报告,试图更好地理解NSCollectionView
和可扩散的数据源。我可以使用基于Reference
的API使其工作,这需要使用NSObject
派生标识符,但尝试使用Swift类、结构或枚举总是导致抛出索引越界异常,即使我基本上使用WWDC的示例代码。这与Apple DTS建议的类似,但是iOS和macOS上的集合视图完全不同,所以如果iOS有相同的问题,那就太糟糕了。