当我们使用一个Picker或者TextField之类的控件时,我们通常使用@State绑定属性。但这只对简单的属性有用,如果我们需要运行一些逻辑来计算当前值,该怎么去处理呢?那就是自定义绑定。
首先,我们可以看一下绑定最简单的形式:
struct ContentView: View {
@State private var selection = 0
var body: some View {
let binding = Binding(
get: {self.selection},
set: {self.selection = $0}
)
Picker("选择", selection: binding) {
ForEach(0..<4) { index in
Text("item\(index)")
}
}
.pickerStyle(DefaultPickerStyle())
}
}
可以看出,改绑定实际上只传递值,它本身不存储或计算任何数据,而只充当我们UI层和正在操作的底层状态值之间的垫片。
值得注意的是,选择器现在使用的是selection: binding,不需要$绑定符来修饰,因为它已经是一个双向绑定了。
其次,我们可以使用高级的自定义绑定,不仅仅是传递单个值。例如,假设我们有一个带有三个切换开关的表单:用户是否同意条款和条件、同意隐私政策以及同意接收有关运输的电子邮件。
我们可以将其表示为三个@State属性:
@State var agreedToTerms = false
@State var agreedToPrivacyPolicy = false
@State var agreedToEmails = false
尽管可以手动切换它们,但我们可以使用自定义绑定一次性完成所有操作。如果这三个布尔值都为真,则此绑定将为真,一旦,则会更新它们,如下所示:
let agreedToAll = Binding(
get: {
self.agreedToTerms && self.agreedToPrivacyPolicy && self.agreedToEmails
},
set: {
self.agreedToTerms = $0
self.agreedToPrivacyPolicy = $0
self.agreedToEmails = $0
}
)
现在我们可以创建四个切换开关:三个用于单个布尔值,一个控制其他三个同时同意或不同意:
struct ContentView: View {
@State var agreedToTerms = false
@State var agreedToPrivacyPolicy = false
@State var agreedToEmails = false
var body: some View {
let agreedToAll = Binding<Bool>(
get: {
self.agreedToTerms && self.agreedToPrivacyPolicy && self.agreedToEmails
},
set: {
self.agreedToTerms = $0
self.agreedToPrivacyPolicy = $0
self.agreedToEmails = $0
}
)
return VStack {
Toggle(isOn: $agreedToTerms) {
Text("Agree to terms")
}
Toggle(isOn: $agreedToPrivacyPolicy) {
Text("Agree to privacy policy")
}
Toggle(isOn: $agreedToEmails) {
Text("Agree to receive shipping emails")
}
Toggle(isOn: agreedToAll) {
Text("Agree to all")
}
}
}
}