[SwiftUI 知识碎片] Debris-4 绑定状态到UI控件

1,142 阅读2分钟
更多内容欢迎关注公众号:Swift花园

SwiftUI的 @State 属性包装器让我们可以自由地修改视图结构体,这意味着当程序状态改变时,我们可以更新属性值来匹配状态。

但是,事情对于UI控件会稍微复杂一些。举个例子,如果创建了一个可编辑文本框,用户可以输入文本,就像这样:

struct ContentView: View {
    var body: some View {
        Form {
            TextField("Enter your name")
            Text("Hello World")
        }
    }
}

上面的代码创建了一个包含一个文本框和一个静态文本的表单。不过,代码无法编译通过。因为 SwiftUI 需要知道你要把文本框的文本存放在哪里。

记得吗?视图是它们状态的函数。文本框只能反映你的程序中存储的某个值。SwiftUI 期望我们的结构体中有一个字符串属性,以便用于显示文本框的内容。

好了,让我们把代码改成这样:

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 需要保证文本框里的字符串跟 name 属性里的字符串始终保持一致,以便满足“视图是它们状态的函数”这个说法——这样一来用户看到的一切其实只是我们代码中结构体和属性的可视化表达。

这正是我将要说到的 ”双向绑定”:我们绑定文本框以便它展示我们的属性值,同时我们也绑定属性以便文本框有任何变化时属性也随之更新。

在 Swift 中,我们用一种特殊的符号标记这种双向绑定,在属性前写一个$符号。它告诉 Swift 不仅需要读取属性的值,也需要在绑定对象的内容改变时,把值写回属性。

因此,正确的代码如下:

struct ContentView: View {
    @State private var name = ""

    var body: some View {
        Form {
            TextField("Enter your name", text: $name)
            Text("Hello World")
        }
    }
}

尝试运行上面的代码,修改文本框里的内容。

为了看到双向绑定的效果,你可以把代码改成这样:

Text("Your name is \(name)")

注意,在静态文本控件里,你只需要用 name 而不是 $name。因为静态文本控件并不需要双向绑定,我们只是读取值。

总结一下,看到$符号,记得它创建了一个双向绑定:属性的值既被读取,也被写入。


我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~