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

SwiftUI中Form删除行触发Index out of range错误而List正常是什么原因?

问题根本原因

1. iOS 14 系统 Form 底层实现缺陷

iOS 14 中 Form 和 List 虽然都基于 UITableView 实现,但二者对动态数组更新的状态同步逻辑存在差异:

  • List 针对 SwiftUI 绑定数组的删除操作做了专门适配,每次数据源变更后会立刻刷新内部 cell 缓存,和最新的数组状态对齐
  • Form 在 iOS 14 没有做对应的适配,删除数组元素后,内部缓存的 cell 索引没有及时更新,仍然会尝试访问已经被删除的索引位置,触发数组越界崩溃。删除最底部行时刚好索引和更新后的数组长度匹配,因此不会触发崩溃。

2. ForEach 标识不稳定放大了问题

你当前使用 ForEach(self.$viewModel.model.components, id: \.self) 以元素自身作为唯一标识,当数组元素发生删除操作时,ForEach 生成的绑定索引本身就会发生偏移,和 Form 残留的旧索引缓存叠加后,直接触发越界报错。


兼容 iOS 14 的解决方案

方案1:使用稳定的唯一标识作为 ForEach 的 ID

将数组元素替换为符合 Identifiable 协议的结构体,避免用元素值/索引作为唯一标识:

// 替换原来的String数组元素
struct Component: Identifiable {
    let id = UUID()
    var content: String
}

struct MyModel {
    var components: [Component]
}

// ForEach写法简化,不需要手动指定id
ForEach($viewModel.model.components) { $component in
    Text(component.content)
}

方案2:手动触发 Form 全量重绘

给 Form 添加 id 修饰符,绑定数组长度,每次数组元素数量变化时强制 Form 完全重绘,清空旧的缓存索引:

Form {
    Section(header: Text("Components")) {
        ForEach(self.$viewModel.model.components, id: \.self) { $component in
            Text(component)
        }
        .onDelete(perform: delete)
    }
}
.id(viewModel.model.components.count) // 新增这行

方案3:升级最低适配版本

该问题在 iOS 15 及以上系统已经被苹果官方修复,如果你的项目后续可以把最低适配升级到 iOS 15+,不需要修改代码也可以正常运行。


内容的提问来源于stack exchange,提问作者Bart van Kuik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 02:48:02