译自 www.hackingwithswift.com/books/ios-s…
在 SwiftUI 中创建自定义绑定
由于 SwiftUI 给属性包装器发送绑定更新的机制,给属性包装器赋予属性观察者是不管用的。也就是说,尽管模糊半径改变了,打印语句也不会被调用:
struct ContentView: View {
@State private var blurAmount: CGFloat = 0 {
didSet {
print("New value is \(blurAmount)")
}
}
var body: some View {
VStack {
Text("Hello, World!")
.blur(radius: blurAmount)
Slider(value: $blurAmount, in: 0...20)
}
}
}为了解决这个问题我们需要创建一个自定义绑定 —— 我们需要直接使用 Binding 结构体,它能让我们在值被读取或者写入时提供自己的代码。
在我们的代码中,我们要一个 Binding 被读取时返回 blurAmount 的值,而被写入时用新值改变 blurAmount 同时也打印出新值,以便我们能看到这个变化过程。此外,不管我们是读取还是写入,都是在对 blurAmount 属性进行操作,而 Swift 是不允许我们创建读取其他属性的属性,因为我们要读取的属性很有可能还没被初始化。
总结一下,我们需要创建一个自定义 Binding,以转嫁 blurAmount,并且在设置它的值时打印消息。另外,我们不能把这个绑定存储为视图的属性,因为从一个属性中读取另一个是不允许的。
因此,我们需要把代码放进视图的 body 属性中,像这样:
struct ContentView: View {
@State private var blurAmount: CGFloat = 0
var body: some View {
let blur = Binding<CGFloat>(
get: {
self.blurAmount
},
set: {
self.blurAmount = $0
print("New value is \(self.blurAmount)")
}
)
return VStack {
Text("Hello, World!")
.blur(radius: blurAmount)
Slider(value: blur, in: 0...20)
}
}
}在我们深入绑定之前,注意到一个细节:我们仍然是使用 @State private var 来声明 blurAmount 属性,用 blur(radius: blurRadius) 作为文本视图的 modifier。
改变之处在于我们声明 slider 里绑定:我们不再使用 $blurAmount,而是用 blur。这是由于,使用 $ 是我们从某个状态中获得双向绑定的方式,而我们现在是直接创建绑定,所以不需要它了。
现在让我们来绑定本身,你应该也能从我们使用它的方式上看出端倪,基本的构造器是像下面这样:
init(get: @escaping () -> Value, set: @escaping (Value) -> Void)依然通过 Cmd+Shift+O 在 SwiftUI 的生成接口中查找 “Binding”。拆解一下,构造器接收两个闭包:一个不接收参数,返回一个值的 getter,以及一个接收一个值,什么也不返回的 setter。Binding 使用的是泛型,因此 Value 实际上是一个占位符,我们可以往里存储任何类型 —— 比如例子中 blur 绑定的 CGFloat。get 和 set 闭包都被标记为 @escaping,表示 Binding 结构体会存储闭包,以便之后使用。
所有这些表明你可以在闭包里做任何事情:调用方法,运行算法计算正确的值,或者使用随机的值 —— 只要你返回一个值。因此,假如你希望在每次值改变时把它写入 UserDefaults,那么一个 Binding 的 set 闭包能完美地满足你的需求。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~