ObservedObject如何订阅其@Published成员

问题描述 投票:0回答:2

我正在尝试编写自己的@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的访问器中通过查看标头)
  • 是否可以进行某种形式的检查来检查每个成员并进行连接,而无需关心ValuePublished<Value>类型是什么?
swiftui observers combine
2个回答
0
投票

应该停留更长的时间-这在某种程度上是可能的(尽管与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()
    }
}

0
投票

尽管我没有回答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
© www.soinside.com 2019 - 2024. All rights reserved.