假如要在 Preview 快捷的运用 Binding 通常会运用 Binding.constant(value)
。但是这个方式让人伤心的当地在于因为是 constant value,所以 get 的时分永远都是初始值。这样我们在 View 内部修改了 Binding 的值,View 不会改写表现出新的值。
比方有我们这样一个开关:
struct BindingPreview: View {
@Binding var value: Bool
var body: some View {
Toggle(isOn: $value) {
Text("开关")
}
.padding()
}
}
在预览视图中,我们点击按钮就不会变成封闭情况。
#Preview("Constant:View不会更新") {
BindingPreview(value: .constant(true))
}
基本思路:包在一个有情况的View里
最清楚明了的思路是我们造一个 wrapper,声明一个情况值作为Binding的值。
struct Wrapper: View {
@State var value: Bool
var body: some View {
BindingPreview(value: $value)
}
}
#Preview("惯例解法") {
Wrapper(value: true)
}
但是以上的代码完全没有通用性。每次运用 Binding 时都要声明一个一次性 wrapper 仍是挺繁琐。我们可以运用泛型把 wrapper 改构成一个通用的 StateWrapper。
struct StateWrapper<Value, Content:View>: View {
@State var value: Value
let content: (_ value: Binding<Value>) -> Content
init(value: Value, @ViewBuilder content: @escaping (_ value: Binding<Value>) -> Content) {
_value = .init(initialValue: value)
self.content = content
}
var body: some View {
content($value)
}
}
#Preview("通用StateWrapper") {
StateWrapper(value: true) { value in
BindingPreview(value: value)
}
}
这样 Binding 的值就可以像情况相同正常运用了。
多参数适配
有的时分还会出现预览的视图有多个 binding 参数,因而可以根据自己的场景写出几个多参数的辅佐办法。
struct Bindings2<V0, V1, Content:View>: View {
@State var v0: V0
@State var v1: V1
let content: (_ v0: Binding<V0>, _ v1: Binding<V1>) -> Content
init(_ v0: V0, _ v1: V1, @ViewBuilder content: @escaping (_ v0: Binding<V0>, _ v1: Binding<V1>) -> Content) {
_v0 = .init(initialValue: v0)
_v1 = .init(initialValue: v1)
self.content = content
}
var body: some View {
content($v0, $v1)
}
}
#Preview("多参数Binding") {
Bindings2(true, "Mike") {
AnotherComplexView(value: $0, name: $1)
}
}
再简化一点:Binding.variable(value)
StateWrapper 在功能上已经到达我们的目的了。但是在运用的时分仍是略显费事。参阅 Binding.constant(value)
,我们也可以直接在 Binding 定义一个静态函数来供应 Binding。
extension Binding {
public static func variable(_ value: Value) -> Binding<Value> {
var state = value
return Binding<Value> {
state
} set: {
state = $0
}
}
}
运用的时分就非常有原生感了:
#Preview("自定义Variable") {
BindingPreview(value: .variable(true))
}
但是 Binding.variable 有一个副作用,因为在函数里不能声明 @State,因而这个方式 binding value 的值改动后,不会触发整个 View 的重新改写。
举个比如:
struct BindingManyView: View {
@Binding var value: Bool
var body: some View {
VStack {
Toggle(isOn: $value) {
Text("开关")
}
Text(value ? "true" : "false")
}
.padding()
}
}
#Preview("自定义Variable副作用") {
BindingManyView(value: .variable(true))
}
可以看到开关 UI 情况虽然会改动,但是下方的 Text 则没有改写。
因而要挑选 StateWrapper 仍是 Binding.variable() 需要根据场景做一下挑选。