You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:30:58