英文:
Prevent SwiftUI from resetting @State
问题
我经常不明白 SwiftUI 何时重置视图的状态(即所有标记为 @State 的内容)。例如,看一下这个最小的示例:
import SwiftUI
struct ContentView: View {
@State private var isView1Active = true
let view1 = View1()
let view2 = View2()
var body: some View {
VStack {
if isView1Active {
view1
} else {
view2
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
@State private var text = ""
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
@State private var text = ""
var body: some View {
TextField("View2: type something...", text: $text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
我希望两个 TextField 保持其内容,但是如果你运行这个示例,会出现一些奇怪的行为:
-
如果你只在 预览 中运行示例,那么只有
View1的TextField内容会保留。 -
相反,如果你在 模拟器 中运行示例(或在 实际设备 上运行),则第一个文本字段和第二个文本字段的内容都不会保留。
所以,这里发生了什么?有没有办法告诉 SwiftUI 不要重置视图的 @State?谢谢。
英文:
I don't often understand when SwiftUI resets the state of a view (i.e. all that is marked with @State). For example, take a look at this minimum example:
import SwiftUI
struct ContentView: View {
@State private var isView1Active = true
let view1 = View1()
let view2 = View2()
var body: some View {
VStack {
if isView1Active {
view1
} else {
view2
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
@State private var text = ""
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
@State private var text = ""
var body: some View {
TextField("View2: type something...", text: $text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'd want the two TextField to keep their content, but if you run this example some weird behaviours occur:
- If you run the example on the preview only the
View1TextFieldcontent persists:
- If you, instead, run the example on the simulator (or on an actual device) neither the first textfield content, nor the second one persist:
So, what's happening here? Is there a way to tell SwiftUI not to reset @State for a view? Thanks.
答案1
得分: 4
问题在于每次更改 isView1Active 时都会重新创建 View1 和 View2(因为它使用了 @State,它会重新加载 ContentView 的内容)。
解决方法是将 TextField 的文本属性保留在 ContentView 中,如下所示,并使用 @Binding:
struct ContentView: View {
@State private var isView1Active = true
@State private var view1Text = ""
@State private var view2Text = ""
var body: some View {
VStack {
if isView1Active {
View1(text: $view1Text)
} else {
View2(text: $view2Text)
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
@Binding var text: String
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
@Binding var text: String
var body: some View {
TextField("View2: type something...", text: $text)
}
}
在操作中显示如下:
英文:
The issue is that View1 and View2 are being recreated every time isView1Active is changed (because it is using @State which reloads the body of ContentView).
A solution would be to keep the text properties of the TextFields in the ContentView as shown below and use @Binding:
struct ContentView: View {
@State private var isView1Active = true
@State private var view1Text = ""
@State private var view2Text = ""
var body: some View {
VStack {
if isView1Active {
View1(text: $view1Text)
} else {
View2(text: $view2Text)
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
}
struct View1: View {
@Binding var text: String
var body: some View {
TextField("View1: type something...", text: $text)
}
}
struct View2: View {
@Binding var text: String
var body: some View {
TextField("View2: type something...", text: $text)
}
}
Shown in action:
答案2
得分: 1
以下是翻译好的部分:
如果 view1 和 view2 完全独立且不包含上下文菜单或底部弹窗,您可以使用 ZStack 和 opacity 的组合。
var body: some View {
VStack {
ZStack {
if isView1Active {
view1.opacity(1)
view2.opacity(0)
} else {
view1.opacity(0)
view2.opacity(1)
}
}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("点击")
})
}
}
英文:
It view1 and view2 are completely independent and enclosure, like there is no contextmenuor sheet, you may use ZStack and opacity combinations.
var body: some View {
VStack {
ZStack{
if isView1Active {
view1.opacity(1)
view2.opacity(0)
} else {
view1.opacity(0)
view2.opacity(1)
}}
Button(action: {
self.isView1Active.toggle()
}, label: {
Text("TAP")
})
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。




评论