SwiftUI两页面Sheet传参异常:一页正常一页数据为空求助
我现在遇到一个SwiftUI的问题:第一个页面的Sheet组件里的AnalysisGraph能正常接收数据,但第二个页面的Sheet里的ShowGraph(功能简化版的AnalysisGraph)始终拿到空数据。为什么第一个页面调用saveAnalysis修改@State变量后能把数据传给AnalysisGraph,第二个页面用setDataToGraph就不行?
页面1代码
@State var analizaId = "" @State var finance = 0.0 @State var value = 0.0 @State var growth = 0.0 // ... 其他代码 ... .sheet(isPresented: $newAnalysis) { AnalysisGraph(clearAnalysis: clear, analizaId: analizaId, finance: finance, value: value, growth: growth, name: name, notes: notes) } // ... 其他代码 ... private func saveAnalysis(){ finance = model.financeCondition() growth = model.growthCondition() value = model.valueCondition() analizaId = model.modelId }
页面2代码
@State var analizaId = "" @State var finance = 0.0 @State var value = 0.0 @State var growth = 0.0 // ... 其他代码 ... .sheet(isPresented: $showAnalysisGraph) { ShowGraph(analizaId: analizaId, finance: finance, value: value, growth: growth, name: name, notes: notes) } // ... 其他代码 ... private func setDataToGraph(){ if let idAnalysis = selected{ for (_, item) in analysisList.enumerated(){ if let idItem = item.id{ if idItem == idAnalysis{ thisAnaliza = item } } } } if let showAnaliza = thisAnaliza, let idOptionalAnaliza = selected, let idAnaliza = idOptionalAnaliza{ print("idAnaliza: \(idAnaliza)") analizaId = idAnaliza finance = showAnaliza.financeAnalysis growth = showAnaliza.growthAnalysis value = showAnaliza.valueAnalysis name = showAnaliza.name ?? "No Name" notes = showAnaliza.notes ?? "No notes" print("analizaId: \(analizaId)") print("\(finance)") print("\(value)") print("\(growth)") print(name) print(notes) showAnalysisGraph.toggle() } }
核心问题
第二个页面的问题在于:你先修改@State变量,紧接着立刻调用showAnalysisGraph.toggle()弹出Sheet。但SwiftUI的@State变量更新是异步的——修改后不会马上触发视图刷新,Sheet会在变量还没完成更新时就被创建,所以拿到的还是旧的空值。
而第一个页面的流程应该是:调用saveAnalysis更新@State后,视图先完成刷新,之后才触发Sheet展示(比如newAnalysis是在其他时机被设为true,不是在同一个方法里刚更新完变量就弹Sheet),所以数据能正常传递。
解决方案
方案1:用sheet(item:)替代sheet(isPresented:)(推荐)
把要传递的数据包装成一个Identifiable的模型对象,Sheet的展示直接和这个模型绑定,当模型不为nil时自动弹出,彻底避免状态不同步问题。
示例代码:
// 先定义数据模型 struct AnalysisData: Identifiable { let id: String let finance: Double let value: Double let growth: Double let name: String let notes: String } // 页面里的状态变量替换成可选模型 @State private var selectedAnalysis: AnalysisData? // Sheet改成如下写法 .sheet(item: $selectedAnalysis) { data in ShowGraph(analizaId: data.id, finance: data.finance, value: data.value, growth: data.growth, name: data.name, notes: data.notes) } // 修改setDataToGraph方法 private func setDataToGraph(){ guard let idAnalysis = selected, let showAnaliza = analysisList.first(where: { $0.id == idAnalysis }), let idAnaliza = showAnaliza.id else { return } selectedAnalysis = AnalysisData( id: idAnaliza, finance: showAnaliza.financeAnalysis, value: showAnaliza.valueAnalysis, growth: showAnaliza.growthAnalysis, name: showAnaliza.name ?? "No Name", notes: showAnaliza.notes ?? "No notes" ) // 无需手动toggle,selectedAnalysis不为nil时Sheet自动弹出 }
这种方式是SwiftUI官方推荐的写法,把Sheet的展示逻辑和数据绑定,从根源上解决状态不同步的问题。
方案2:延迟弹出Sheet(临时 workaround)
如果不想修改现有结构,可以在修改完@State变量后,用DispatchQueue.main.async延迟触发Sheet,给SwiftUI时间完成状态更新:
private func setDataToGraph(){ // ... 原有的数据赋值逻辑保持不变 ... // 替换原来的showAnalysisGraph.toggle() DispatchQueue.main.async { showAnalysisGraph = true } }
但这种方法属于临时解决方案,后续如果有复杂的状态变化,还是容易出现数据不同步的问题,优先推荐方案1。
额外优化建议
- 页面2里查找
thisAnaliza的循环可以简化成analysisList.first(where: { $0.id == idAnalysis }),不用手动遍历枚举,代码更简洁高效。 - 尽量避免在同一个方法里同时修改@State变量和触发Sheet展示,这种操作很容易因为SwiftUI的异步更新机制导致数据不同步。
内容的提问来源于stack exchange,提问作者Mamazur

