为什么SwiftUI中sheet在数据赋值前就打开引发崩溃?
问题原因
这个问题的本质是 SwiftUI 中 @State 属性的更新机制和 sheet(isPresented:) 构造器的内容闭包求值时机不匹配导致的,具体逻辑如下:
- @State 属性的变更不是同步生效的。你在网络回调中先后对
dataToSend和openSheet赋值,两个变更会被合并到当前 RunLoop 结束后的同一次视图渲染周期中才会正式生效,赋值后立即读取属性拿到的还是变更前的旧值。 - sheet(isPresented:) 的内容闭包会在
isPresented绑定值变为true的瞬间立即求值创建视图,不会等到本次渲染周期完成、所有 @State 变更都生效后再创建。这就导致闭包捕获到的dataToSend还是变更前的nil,你声明的是隐式解包可选类型,直接访问就触发了崩溃。
你观察到的两个现象也能印证这个逻辑:
- 延迟1秒再修改
openSheet:此时已经经过了多个 RunLoop 周期,dataToSend的变更早就完成生效,闭包读取到的就是非空值,不会崩溃。 - 给
dataToSend设空数组初始值:此时不会触发解包崩溃,但sheet(isPresented:)的内容一旦创建就不会跟随 @State 变更自动刷新,所以即使后续dataToSend拿到了接口返回的正确数据,已经创建好的MyView也不会更新,一直显示初始的空数组。
解决方案
最优的解决方案是改用 sheet(item:) 构造器处理带数据的弹窗场景,完全避免手动管理两个关联状态的同步问题:
struct MyApp: App { // 改为普通可选类型,去掉隐式解包,不需要额外维护openSheet状态 @State var dataToSend: [CustomClass]? var body: some Scene { WindowGroup { TabView { HomeView() SearchView() SettingsView() } // 使用sheet(item:)构造器,自动关联数据和弹窗状态 .sheet(item: $dataToSend) { data in MyView(data: data) } .onOpenURL { url in ApiCall.getData(from: url) { dataFromApi in // 只需要给dataToSend赋值即可,弹窗会自动弹出 self.dataToSend = dataFromApi } } } } }
sheet(item:) 会自动在 item 不为 nil 时弹出弹窗,并且把非空的 item 直接传入内容闭包,完全不会出现空值问题。
内容的提问来源于stack exchange,提问作者swifthing
相关产品推荐
相关产品推荐

