SwiftUI:TabView与NavigationStack嵌套时Sheet内TextField后台重置问题
问题原因与解决方法
问题原因
SwiftUI中,@State是绑定到视图自身的状态存储,当视图被销毁并重新创建时,@State会自动重置为初始值。在TabView+NavigationStack的组合结构下,应用前后台切换时,系统可能触发视图层级的重新计算,导致FormView所在的sheet内容被销毁后重建——由于FormView的输入状态是自身持有的@State,重建后自然丢失之前的输入内容。
而Form 2的输入状态存在父视图ContentView的@State里,父视图在前后台切换时不会被销毁,因此状态能稳定保留。另外,移除TabView或NavigationStack任意一个组件后,视图层级的稳定性提升,sheet的内容不会被触发重建,状态也就不会丢失。
解决方法
方法1:将状态提升到父视图(适合简单场景)
把FormView的输入状态移到父视图,通过绑定传递给子视图,让状态的生命周期和父视图绑定,不受子视图重建影响:
struct ContentView: View { @State var isShowingForm1 = false @State var isShowingForm2 = false @State var email = "" @State var password = "" var body: some View { TabView { NavigationStack { List { Button("Form 1") { isShowingForm1 = true } .sheet(isPresented: $isShowingForm1) { FormView(email: $email, password: $password) .padding() } Button("Form 2") { isShowingForm2 = true } .sheet(isPresented: $isShowingForm2) { VStack { TextField("Email", text: $email) SecureField("Password", text: $password) } .padding() } } } .tabItem { Label("Tab1", systemImage: "circle") } } } } struct FormView: View { @Binding var email: String @Binding var password: String var body: some View { VStack { TextField("Email", text: $email) SecureField("Password", text: $password) } } }
方法2:使用@StateObject管理子视图状态(适合复杂子视图)
如果希望状态由FormView自身管理,可以用@StateObject存储状态——@StateObject的生命周期独立于视图重建,只要视图没有被完全移除,状态对象就会保留:
class FormViewModel: ObservableObject { @Published var email = "" @Published var password = "" } struct ContentView: View { @State var isShowingForm1 = false @State var isShowingForm2 = false @State var email = "" @State var password = "" var body: some View { TabView { NavigationStack { List { Button("Form 1") { isShowingForm1 = true } .sheet(isPresented: $isShowingForm1) { FormView() .padding() } Button("Form 2") { isShowingForm2 = true } .sheet(isPresented: $isShowingForm2) { VStack { TextField("Email", text: $email) SecureField("Password", text: $password) } .padding() } } } .tabItem { Label("Tab1", systemImage: "circle") } } } } struct FormView: View { @StateObject private var viewModel = FormViewModel() var body: some View { VStack { TextField("Email", text: $viewModel.email) SecureField("Password", text: $viewModel.password) } } }
方法3:调整sheet的挂载层级
把sheet从List的Button上移到更上层的视图(比如NavigationStack),减少因List行重建导致的sheet内容重置:
struct ContentView: View { @State var isShowingForm1 = false @State var isShowingForm2 = false @State var email = "" @State var password = "" var body: some View { TabView { NavigationStack { List { Button("Form 1") { isShowingForm1 = true } Button("Form 2") { isShowingForm2 = true } } .sheet(isPresented: $isShowingForm1) { FormView() .padding() } .sheet(isPresented: $isShowingForm2) { VStack { TextField("Email", text: $email) SecureField("Password", text: $password) } .padding() } } .tabItem { Label("Tab1", systemImage: "circle") } } } } struct FormView: View { @State var email = "" @State var password = "" var body: some View { VStack { TextField("Email", text: $email) SecureField("Password", text: $password) } } }
内容的提问来源于stack exchange,提问作者Peyman
相关产品推荐
相关产品推荐

