我正在尝试编写自己的@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
在第3个抽头上用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
的{{1}}类型是什么? 答案 0 :(得分:1)
应该坚持更长的时间-这在某种程度上是可能的(尽管与Published
的使用方式不同)
以下代码更新了上面的代码,以包括一个Void
类型的valueWillChange
发布者,以允许相同的流程正常工作。而且我没有意识到Swift内置了Mirror
反射。
我不能说我知道ObservedObject
的工作方式 在没有总体协议或对Void值隐瞒某些发行商的情况下–也许怎么将其作为{{ 1}}类型,却不知道Published<Value>
是什么(或忽略它)
这也不难适应可能需要的任何其他条件(小于值验证等)
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
{
willSet
{
if wrappedValue != newValue
{
_valueWillChange.send()
}
}
didSet
{
if oldValue != wrappedValue
{
_projectedValue.send(wrappedValue)
}
}
}
public var projectedValue: AnyPublisher<Value, Never>
public var valueWillChange: AnyPublisher<Void, Never>
private var _projectedValue: CurrentValueSubject<Value, Never>
private var _valueWillChange: CurrentValueSubject<Void, 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.eraseToAnyPublisher()
self._valueWillChange = CurrentValueSubject<Void, Never>()
self.valueWillChange = self._valueWillChange.eraseToAnyPublisher()
}
}
答案 1 :(得分:0)
尽管我没有回答 ObservedObject如何订阅其@Published成员的实际问题?,但我可以向您展示与your answer不同的方法来实现您想要实现的目标。
如果我没记错的话,则您尝试订阅objectWillChange
发布者作为您的自定义属性包装器类型。默认情况下,@Published
包装器会自动为您执行此操作。要使用您的包装器类型,您只需手动调用send()
上的objectWillChange
。因此,您需要订阅定义的属性包装的发布者(预测值),然后在可用的send()
发布者上调用objectWillChange
方法。
class Model: ObservableObject {
@EqPublished var valueEq = 5
@Published var valueAlways = 5
private var storage = Set<AnyCancellable>()
init() {
$valueEq
.sink { [weak self] _ in
self?.objectWillChange.send()
}
.store(in: &storage)
}
}