SwiftUI中NavigationSplitView删除选中项时崩溃问题求助
问题场景
在NavigationSplitView的左侧列表中展示存储的条目,删除**当前处于选中状态(右侧详情区正在展示其内容)**的条目时应用崩溃,删除未选中条目则无异常。删除操作通过List的.onDelete事件实现。
代码实现
var body: some View { @Bindable var nc = navigationContext NavigationSplitView { List(selection: $nc.selectedTimeline) { ForEach(timelines){ timeline in NavigationLink(timeline.name, value: timeline) } .onDelete(perform: removeTimelines) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { Button(action: {showNewTimeline = true}) { Label("Add Timeline", systemImage: "plus") } } ToolbarItem { Button(action: {showEditTimeline = true}) { Label("Edit Timeline", systemImage: "pencil") } } } } detail: { TimelineView(timeline: nc.selectedTimeline) .environment(navigationContext) } .sheet(isPresented: $showNewTimeline) { TimelineEditorView(timeline: nil) } .sheet(isPresented: $showEditTimeline) { TimelineEditorView(timeline: navigationContext.selectedTimeline) } } func removeTimelines(at indexSet: IndexSet) { print("Deleting timelines...") for index in indexSet { let timelineToDelete = timelines[index] if navigationContext.selectedTimeline?.persistentModelID == timelineToDelete.persistentModelID { navigationContext.selectedTimeline = nil } modelContext.delete(timelineToDelete) } }
报错信息(中文翻译)
*** 在 -[SwiftUI.UpdateCoalescingCollectionView validateSelectionOrHighlightIndexPath:isSelection:allowMassIndexPath:assertIfInvalid:] 中断言失败,UICollectionView.m:8266
尝试修改超出边界的条目(索引1)的选中状态,但第0分区仅有1个条目。集合视图信息:<SwiftUI.UpdateCoalescingCollectionView: 0x13480e600; baseClass = UICollectionView; frame = (0 0; 420 998); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x600001898390>; backgroundColor = UIExtendedGrayColorSpace 0 0; layer = <CALayer: 0x6000016949c0>; contentOffset: {0, -70}; contentSize: {420, 54}; adjustedContentInset: {70, 0, 0, 0}; layout: <UICollectionViewCompositionalLayout: 0x133e15090>; dataSource: <TtGC7SwiftUI31UICollectionViewListCoordinatorGVS_28CollectionViewListDataSourceC12Timeline_iOS8Timeline_GOS_19SelectionManagerBoxS3: 0x135870160>>
修复方案
崩溃根源是删除选中条目时,List底层的UICollectionView在数据源更新后,仍试图维护已失效的选中索引,导致索引越界。以下是两种可行修复方法:
方法1:异步清空选中状态
将清空选中状态的操作放到主队列异步执行,确保数据源更新完成后再处理选中状态:
func removeTimelines(at indexSet: IndexSet) { print("Deleting timelines...") for index in indexSet { let timelineToDelete = timelines[index] if navigationContext.selectedTimeline?.persistentModelID == timelineToDelete.persistentModelID { DispatchQueue.main.async { self.navigationContext.selectedTimeline = nil } } modelContext.delete(timelineToDelete) } }
方法2:用动画块包裹删除操作
通过withAnimation协调UI更新,确保选中状态与数据源变更同步:
func removeTimelines(at indexSet: IndexSet) { print("Deleting timelines...") withAnimation { for index in indexSet { let timelineToDelete = timelines[index] if navigationContext.selectedTimeline?.persistentModelID == timelineToDelete.persistentModelID { navigationContext.selectedTimeline = nil } modelContext.delete(timelineToDelete) } } }
额外优化建议
- 确保
timelines数组的元素遵循Identifiable协议,或在ForEach中明确指定唯一ID(如ForEach(timelines, id: \.persistentModelID)),避免SwiftUI错误识别条目。 - 确认
navigationContext.selectedTimeline的绑定类型与List的选中值类型完全匹配。
内容的提问来源于stack exchange,提问作者Paul Martin

