SwiftUI可选@State传递给非可选@Binding崩溃的正确传值方法
问题根因
崩溃来自两个逻辑错误:
- 你把
.sheet修饰符写在了ForEach上,SwiftUI初始化列表时就会提前执行sheet的内容构造闭包,此时editingCar的初始值是nil,你写的Binding($editingCar)!强制解包nil直接触发崩溃,和你点击编辑按钮后给editingCar赋值的逻辑无关——你打印的赋值成功日志是后续交互阶段的输出,远晚于视图初始化阶段的崩溃触发时机。 - 就算绕过崩溃,你当前的实现也无法把编辑结果同步回原数组:
Car是值类型,赋值给@State var editingCar时生成的是独立拷贝,和vm.cars里的原数据没有绑定关系,编辑内容不会更新列表。
修正方案
- 把
.sheet修饰符移到List层级,不要挂在ForEach上,避免列表滚动、初始化时提前触发sheet构造逻辑 - 删掉强制解包可选Binding的写法,在sheet的内容闭包里,根据返回的编辑项id从数据源中找到对应位置,生成直接绑定原数组元素的非可选
Binding传给子视图,既避免解包崩溃,又能实时同步编辑结果 - 删掉手动重置
editingCar为nil的方法,.sheet(item:)会在弹窗关闭时自动将绑定的可选值置空,不需要手动调用触发状态刷新
修正后的完整可运行代码:
import SwiftUI struct Car: Identifiable { let id = UUID().uuidString var model: String var color: String } class CarModelView: ObservableObject { @Published var cars: [Car] = [ .init(model: "Audi A8", color: "Red"), .init(model: "Honda Civic", color: "Blue"), .init(model: "BMW M3", color: "Black"), .init(model: "Toyota Supra", color: "Orange") ] } struct CarListView: View { @StateObject var vm = CarModelView() @State var editingCar: Car? var body: some View { List { ForEach(vm.cars) { car in VStack(alignment: .leading) { Text(car.model) .font(.headline) Text(car.color) .font(.callout) } .swipeActions(edge: .trailing) { Button { editingCar = car } label: { Label("Edit", systemImage: "pencil.circle") } } } } .sheet(item: $editingCar) { car in guard let index = vm.cars.firstIndex(where: { $0.id == car.id }) else { return } SheetView(editingCar: $vm.cars[index]) } .environmentObject(vm) } } struct SheetView: View { @Binding var editingCar: Car var body: some View { VStack(spacing: 16) { Text("Edit Car Data") .font(.title2) TextField("Model", text: $editingCar.model) .textFieldStyle(.roundedBorder) .padding(.horizontal) TextField("Color", text: $editingCar.color) .textFieldStyle(.roundedBorder) .padding(.horizontal) Spacer() } .padding(.top, 32) } }
补充说明:SwiftUI是声明式UI框架,所有挂载在视图上的修饰符闭包不会等到交互触发时才执行,会在视图首次渲染时就参与构造求值,任何写在视图构造层级的强制解包、隐式解包操作,只要初始状态不满足非空要求,都会立刻触发运行时崩溃,不要在视图体里写强制解包逻辑。
内容的提问来源于stack exchange,提问作者Nizami
相关产品推荐
相关产品推荐

