SwiftUI的@State
属性包装器允许我们自由修改视图结构体,这意味着当程序更改时,我们可以更新视图属性以匹配。
但是,使用UI控件时,事情会更复杂一些。例如,如果要创建用户可以键入的可编辑文本框,可以创建如下所示的快速用户界面视图:
代码语言:javascript复制struct ContentView: View {
var body: some View {
Form {
TextField("Enter your name")
Text("Hello World")
}
}
}
尝试创建包含文本字段和文本视图的窗体。但是,该代码不会编译,因为SwiftUI想知道文本字段中的文本存储位置。
请记住,视图是其状态的函数——文本输入框只能在反映存储在程序中的值时显示某些内容。SwiftUI需要的是结构中的一个字符串属性,它可以显示在文本输入框中,还将存储用户在文本输入框中键入的任何内容。
所以,我们可以把代码改成:
代码语言:javascript复制struct ContentView: View {
var name = ""
var body: some View {
Form {
TextField("Enter your name", text: name)
Text("Hello World")
}
}
}
这将添加一个name
属性,然后使用它创建文本字段。但是,该代码仍然无法工作,因为Swift需要能够更新name
属性以匹配用户在文本字段中键入的任何内容,因此您可以使用`@State``,如下所示:
@State private var name = ""
但这还不够,我们的代码仍然无法编译。
问题是Swift区分了“在此处显示此属性的值”和“在此处显示此属性的值,但将任何更改写回该属性”
在Swift中,我们用一个特殊的符号来标记这些双向绑定,这样它们就很显眼:我们在它们前面写一个美元符号$
。这告诉Swift,它应该读取属性的值,但也应该在发生任何更改时将其写回。
所以,我们的结构体的正确版本是:
代码语言:javascript复制struct ContentView: View {
@State private var name = ""
var body: some View {
Form {
TextField("Enter your name", text: $name)
Text("Hello World")
}
}
}
现在试着运行这个代码——你应该发现你可以点击文本字段并输入你的名字,如预期的那样。
在继续之前,让我们修改文本视图,使其在文本字段的正下方显示用户名:
代码语言:javascript复制Text("Your name is (name)")
注意它是如何使用name
而不是$name
?这是因为我们不想在这里使用双向绑定——我们想读取值,是的,但我们不想以某种方式将其写回,因为文本视图不会改变。
因此,当您在属性名称前看到一个美元符号时,请记住它创建了一个双向绑定:属性的值是读的,也是写的。
Binding state to user interface controls