SwiftUI首次从父视图传值至SecondView时出现值错误问题求助
问题重现
通过分段选择器修改布尔值isRunning的状态后,首次点击按钮打开SecondView时,视图接收到的是初始值true而非修改后的false;但关闭视图后重新切换状态再打开,就能获取正确值。
主视图代码
struct ContentView: View { @State private var onOffSegments = 1 @State private var isRunning = true @State private var secondViewIsPresented = false var body: some View { VStack { Picker("", selection: $onOffSegments) { Text("Started") .tag(1) Text( "Turned Off") .tag(2) } .pickerStyle(.segmented) .onChange(of: onOffSegments, perform: { value in if value == 1{ isRunning = true print("Is Running:\(isRunning)") }else{ isRunning = false print("Is Running: \(isRunning)") } }) Button("Show Second View"){ secondViewIsPresented = true } } .padding() .sheet(isPresented: $secondViewIsPresented){ SecondView(isRunning: isRunning) } } }
第二个视图代码
struct SecondView: View { var isRunning: Bool var body: some View { Text("Second View") .onAppear{ print("OnAppear SecondView, Is Running: \(isRunning)") } } }
问题原因
SwiftUI中使用sheet(isPresented:)时,闭包内的视图会在父视图首次渲染时就被创建,并捕获当时的isRunning值。首次启动应用时isRunning初始为true,这个值被SecondView实例保存;后续修改isRunning时,已创建的SecondView实例不会重新捕获新值,所以第一次打开时仍用初始值。而关闭视图后,该实例被销毁,再次打开时会重新创建SecondView,此时才会捕获最新的isRunning值,因此第二次能得到正确结果。
解决方案
方案1:用sheet(item:)替代sheet(isPresented:)
通过绑定可选值触发sheet,每次打开都会重新创建视图并捕获最新状态:
struct ContentView: View { @State private var onOffSegments = 1 @State private var isRunning = true @State private var secondViewTrigger: Bool? var body: some View { VStack { Picker("", selection: $onOffSegments) { Text("Started").tag(1) Text("Turned Off").tag(2) } .pickerStyle(.segmented) .onChange(of: onOffSegments) { value in isRunning = value == 1 print("Is Running:\(isRunning)") } Button("Show Second View"){ secondViewTrigger = isRunning } } .padding() .sheet(item: $secondViewTrigger) { currentValue in SecondView(isRunning: currentValue) } } } extension Bool: Identifiable { public var id: Self { self } }
方案2:让SecondView接收绑定值
将isRunning改为绑定类型,让视图实时读取最新状态:
struct SecondView: View { @Binding var isRunning: Bool var body: some View { Text("Second View") .onAppear{ print("OnAppear SecondView, Is Running: \(isRunning)") } } } // 主视图中传递绑定 .sheet(isPresented: $secondViewIsPresented){ SecondView(isRunning: $isRunning) }
方案3:用ObservableObject管理状态
将状态放到引用类型的对象中,利用其特性让视图感知状态变化:
class AppState: ObservableObject { @Published var isRunning = true } struct ContentView: View { @State private var onOffSegments = 1 @StateObject private var appState = AppState() @State private var secondViewIsPresented = false var body: some View { VStack { Picker("", selection: $onOffSegments) { Text("Started").tag(1) Text("Turned Off").tag(2) } .pickerStyle(.segmented) .onChange(of: onOffSegments) { value in appState.isRunning = value == 1 print("Is Running:\(appState.isRunning)") } Button("Show Second View"){ secondViewIsPresented = true } } .padding() .sheet(isPresented: $secondViewIsPresented){ SecondView(appState: appState) } } } struct SecondView: View { @ObservedObject var appState: AppState var body: some View { Text("Second View") .onAppear{ print("OnAppear SecondView, Is Running: \(appState.isRunning)") } } }
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

