英文:
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
View1
TextField
content 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 TextField
s 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 contextmenu
or 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")
})
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论