ValueBinder 创建一个双向绑定对象,允许对象之间共享值。
这与 SwiftUI 中的 @Binding
有些类似,但不依赖于 SwiftUI - 这意味着它几乎可以在 Swift 可以使用的任何地方使用。
您可以使用标准初始化程序来定义一个 binder。
此初始化程序还允许您提供一个回调块,该回调块在 wrappedValue
更改时触发。
let countBinder = ValueBinder<Int>(0) { newValue in
Swift.print("countBinder changed: \(newValue)")
}
...
countBinder.wrappedValue = 4 // triggers the update callback
您可以将 ValueBinding 对象传递给另一个类,该类可以提供一个代码块,以便在 ValueBinder
的包装值更改时调用该代码块。
class AnotherClass {
init(_ binder: ValueBinder<Int>) {
binder.register(self) { newValue in
Swift.print("Binding detected change: \(newValue)")
}
}
}
此外,如果您保留 binder 对象,您的类也可以更新 ValueBinder 的值!
class AnotherClass {
let countBinder: ValueBinder<Int>
init(_ binder: ValueBinder<Int>) {
countBinder = binder
countBinder.register(self) { newValue in
Swift.print("Binding detected change: \(newValue)")
}
}
func userPressed() {
countBinder.wrappedValue += 1
}
}
任何拥有 ValueBinder
对象的对象都可以更新包装值。
_countBinder.wrappedValue += 1
所有已注册更改回调的对象都将收到值更改的通知。
ValueBinding
是 ValueBinder
类型的属性包装器实现。 感谢 Mx-Iris 分享他们的实现。
@ValueBinding var countValue = 0
// Register a block for updates
$countValue.register { newValue in
Swift.print("countValue is now \(newValue)")
}
countValue = 4 // triggers the update callback
// prints "countValue is now 4"
// Register for combine updates
$countValue.publisher?.publisher
.receive(on: DispatchQueue.global(qos: .background))
.sink { newValue in
// Do something with 'newValue'
}
.store(in: &subscribers)
KeyPathBinder
是 ValueBinder
的一种特殊化,可以跟踪动态 keypath。
// The dynamic property to bind to. This might be (for example) bound to a control from interface builder.
@objc dynamic var state: NSControl.State = .on
// Our binding object
lazy var boundKeyPath: KeyPathBinder<MyViewController, NSControl.StateValue> = {
return try! .init(self, keyPath: \.buttonState) { newValue in
Swift.print("boundKeyPath notifies change: \(String(describing: newValue))")
}
}()
EnumKeyPathBinder
是一个 keypath binder,用于观察 Swift enum
类型。
我遇到了这样一种情况:我试图在工具栏的大小模式上使用 KeyPathBinder
,该模式的类型为 NSToolbar.SizeMode
,但一直失败。 但是,绑定到控件上的 NSControl.StateValue
却可以正常工作。
结果是 NSControl.StateValue
虽然看起来像一个枚举,但实际上是一个结构体,而 NSToolbar.SizeMode
是一个枚举(特别是 RawRepresentable)。 当尝试观察枚举类型更改时会出现问题,因此该类是 KeyPathBinder
的一种特殊化,专门用于观察此类枚举类型。
两种 binder 类型都公开了一个属性 publisher
,您可以将其连接到您的 combine 工作流程。
如果操作系统不支持 Combine,则 publisher
属性将为 nil。
let binder = ValueBinder(0)
...
let cancellable = binder.publisher?.sink { newValue in
// do something with `newValue`
}
MIT License
Copyright (c) 2023 Darren Ford
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.