当您使用属性包装器时,您可以访问两者$varName
,_varName
而我并没有真正明白其中的区别。例如,这里
import SwiftUI
struct ContentView: View {
@Binding var varName: String
var body: some View {
TextField("", text: $varName) //here you can also use `_varName`
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(varName: .constant("Hello world!"))
}
}
#endif
Run Code Online (Sandbox Code Playgroud)
您可以同时使用$varName
和_varName
。这两种解决方案似乎是等价的。两个变量都是Binding<String>
。但如果我需要这样的东西:
import SwiftUI
struct ContentView: View {
@Binding var varName: String
init(varName: Binding<String>) {
self.$varName = varName //ERROR
}
var body: some View {
TextField("", text: $varName)
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(varName: .constant("Hello world!"))
}
}
#endif
Run Code Online (Sandbox Code Playgroud)
我会得到一个错误:
无法分配给属性:“$varName”是不可变的
我必须使用_varName
以抑制错误:
struct ContentView: View {
@Binding var varName: String
init(varName: Binding<String>) {
self._varName = varName //this works fine
}
var body: some View {
TextField("", text: _varName)
}
}
Run Code Online (Sandbox Code Playgroud)
They are still both Binding<String>
, so why won't the former solution work? According to Apple (https://developer.apple.com/videos/play/wwdc2019/415/) the compiler will turn a property wrapper into two things. This:
@Binding var varName: String
Run Code Online (Sandbox Code Playgroud)
becomes:
//Compiler-synthesized code
var $varName = Binding<String> = Binding<String>()
public var varName: String {
get { $varName.wrappedValue }
set { $varName.wrappedValue = newValue }
}
Run Code Online (Sandbox Code Playgroud)
$varName
should be var
, so why the error above? And, above all, what is that _varName
? Where does it come from?
合成_varName
属性是一个存储的、可设置的属性,它保存(在您的情况下)Binding<String>
.
该varName
属性映射到包装器的wrappedValue
属性。Binding
声明wrappedValue
如下:
Run Code Online (Sandbox Code Playgroud)var wrappedValue: Value { get nonmutating set }
因为wrappedValue
是用 声明的nonmutating set
,所以合成的varName
属性总是可设置的(即使self
是不可变的)。
如果包装器具有属性,则合成$varName
属性将映射到包装projectedValue
器的projectedValue
属性。Binding
声明projectedValue
如下:
Run Code Online (Sandbox Code Playgroud)var projectedValue: Binding<Value> { get }
由于projectedValue
只是声明get
,而不是get set
,你永远不能分配给$varName
.
Binding
并不需要提供一个projectedValue
属性,因为你可以使用_varName
来获取Binding<String>
对象。究其原因Binding
声明一个projectedValue
属性,使$
前缀工作以同样的方式Binding
,因为它确实为State
,ObservedObject
和EnvironmentObject
。
归档时间: |
|
查看次数: |
443 次 |
最近记录: |