我正在尝试编写自己的@Published
之类的属性包装器-我有数百个成员值,我只希望在它们实际更改相等性时才发出发布值(与每次设置时相反,这是默认)
属性包装器本身很简单
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper public struct EqPublished<Value: Equatable> {
public var wrappedValue: Value
{
willSet {
_projectedValue.send(newValue)
}
}
public var projectedValue: AnyPublisher<Value, Never>
private var _projectedValue: CurrentValueSubject<Value, Never>
public init(wrappedValue: Value)
{
self.wrappedValue = wrappedValue
self._projectedValue = CurrentValueSubject<Value, Never>(wrappedValue)
self.projectedValue = _projectedValue.removeDuplicates().eraseToAnyPublisher()
}
}
可以与类似的东西一起使用
class Model: ObservableObject {
@EqPublished var value = 5
}
// In some view later on
@State var count = 0
...
Text("El Tappo")
.onTapGesture {
// Update every 3rd tap
self.count = (self.count + 1) % 3
self.model.value = self.model.value + (self.count == 0 ? 1 : 0)
}
.onReceive(self.model.$value) { val in
print("Value is \(val)")
}
按预期工作,onReceive
在第三个抽头上用6触发。EqPublished
替换为Published
,每次都调用它。
但是-如果我将使用视图更改为
Text("Value is \(model.value)")
.onTapGesture {
// Update every 3rd tap
self.count = (self.count + 1) % 3
self.model.value = self.model.value + (self.count == 0 ? 1 : 0)
}
.onReceive(self.model.objectWillChange) { _ in
print("Model")
}
objectWillChange
发布者不会触发,UI也不会重新计算-如果我将@EqPublished
更改为简单的@Published
,则所有操作都可以进行。
问题-已更新,以加深理解
ObservedObject
类如何将自身连接到其发布成员? objectWillChange
的访问器中通过查看标头)Value
的Published<Value>
类型是什么? 应该停留更长的时间-这在某种程度上是可能的(尽管与Published
的使用方式不同)
以下代码更新了上面的代码,以包含Void
类型的valueWillChange
发布者,以允许相同的流程正常工作。而且我没有意识到Swift内置了Mirror
反射功能。
我不能说我知道ObservedObject
的工作方式没有一个无效的值的总体协议或某种隐藏的发布者-或者也许如何在不知道什么的情况下将其投射为Published<Value>
类型[ C0]是(或忽略它)
这也不难适应可能需要的任何其他条件(小于值验证等)
Value
并更新了UI代码以显示其用法
class Model: EqObservableObject {
@EqPublished var valueEq = 5
@Published var valueAlways = 5
}
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
public class EqObservableObject : Combine.ObservableObject {
private var eqValuesChanging: [AnyCancellable] = []
init()
{
let mirror = Mirror(reflecting: self)
for child in mirror.children
{
if let value = child.value as? EqPublishedProtocol
{
eqValuesChanging.append(
value.valueWillChange.sink(receiveValue: { [weak self] in
self?.objectWillChange.send()
})
)
}
}
}
}
public protocol EqPublishedProtocol
{
var valueWillChange: AnyPublisher<Void, Never> { get }
}
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@propertyWrapper public struct EqPublished<Value: Equatable>: EqPublishedProtocol
{
public var wrappedValue: Value
{
didSet
{
_projectedValue.send(wrappedValue)
}
}
public var projectedValue: AnyPublisher<Value, Never>
public var valueWillChange: AnyPublisher<Void, Never>
private var _projectedValue: CurrentValueSubject<Value, Never>
/// Initialize the storage of the Published property as well as the corresponding `Publisher`.
public init(wrappedValue: Value)
{
self.wrappedValue = wrappedValue
self._projectedValue = CurrentValueSubject<Value, Never>(wrappedValue)
self.projectedValue = _projectedValue.removeDuplicates().eraseToAnyPublisher()
self.valueWillChange = self.projectedValue.map { _ in }.eraseToAnyPublisher()
}
}
尽管我没有回答ObservedObject如何订阅其@Published成员?的实际问题,但我可以向您展示与 HStack {
Text("ValueEq is \(model.valueEq)")
.onTapGesture {
self.count = (self.count + 1) % 3
self.model.valueEq = self.model.valueEq + (self.count == 0 ? 1 : 0)
}
.onReceive(self.model.$valueEq) { val in
print("ValueEq is \(val)")
}
Text("ValueAlways is \(model.valueAlways)")
.onTapGesture {
self.count = (self.count + 1) % 3
self.model.valueAlways = self.model.valueAlways + (self.count == 0 ? 1 : 0)
}
.onReceive(self.model.$valueAlways) { val in
print("ValueAlways is \(val)")
}
}
.onReceive(self.model.objectWillChange) { _ in
print("Object Changed")
}
不同的方法来实现您想要的目标。
[如果我没记错的话,您正在尝试为自定义属性包装器类型订阅your answer发布者。默认情况下,objectWillChange
包装器会自动为您执行此操作。要使它与包装类型一起使用,您只需要手动调用@Published
上的send()
。因此,您需要订阅定义的属性包装的发布者(预测值),然后在可用的objectWillChange
发布者上调用send()
方法。
objectWillChange