首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >@已发布的var名称: ClassType` `不工作_outside_ of SwiftUI /手动触发器?

@已发布的var名称: ClassType` `不工作_outside_ of SwiftUI /手动触发器?
EN

Stack Overflow用户
提问于 2020-06-05 09:51:08
回答 3查看 251关注 0票数 1

我发现了很多与SwiftUI相关的主题,但这些都没有帮助(比如Why an ObservedObject array is not updated in my SwiftUI application?)

这不适用于Swift中的组合(特别是而不是使用SwiftUI的):

代码语言:javascript
运行
复制
class SomeTask {
  @Published var progress = Progress(totalUnitCount: 5) // Progress is a Class
  [...]
}
var task = SomeTask()
let cancellable = task.$progress.sink { print($0.fractionCompleted) }
task.progress.completedUnitCount = 2

这与SwiftUI无关,所以没有ObservableObject继承来获取objectWillChange,但是即使我尝试使用ObservableObjecttask.objectWillChange.send(),它也什么也不做,也尝试添加extension Progress: ObservableObject {}也没有帮助。由于发行者通过var的willSet发出值,并且由于Progress本身是类类型,所以不会发生任何事情。

似乎没有真正合适的方法来手动触发它?

只有我找到的解决方案是重新分配自己,这是相当尴尬的:

let pr = progress progress = pr

(编写progress = progress是编译时错误)。

唯一可行的方法可能是使用Key-value-observing/KVO和/或编写一个新的@PublishedClassType属性包装器?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-06-06 08:12:01

我能够使用由@propertyWrapper包装的KVO实现这一功能,CurrentValueSubject作为发布服务器:

代码语言:javascript
运行
复制
@propertyWrapper
class PublishedClass<T : NSObject> {
    private let subject: CurrentValueSubject<T, Never>
    private var observation: NSKeyValueObservation? = nil

    init<U>(wrappedValue: T, keyPath: ReferenceWritableKeyPath<T, U>) {
        self.wrappedValue = wrappedValue
        subject = CurrentValueSubject(wrappedValue)
        observation = wrappedValue.observe(keyPath, options: [.new]) { (wrapped, change) in
            self.subject.send(wrapped)
        }
    }

    var wrappedValue: T

    var projectedValue: CurrentValueSubject<T, Never> {
        subject
    }

    deinit {
        observation.invalidate()
    }
}

用法:

代码语言:javascript
运行
复制
class Bar : NSObject {
    @objc dynamic var a: Int
    init(a: Int) {
        self.a = a
    }
}

class Foo {
    @PublishedClass(keyPath: \.a)
    var bar = Bar(a: 0)
}

let f = Foo()
let c = f.$bar.sink(receiveValue: { x in print(x.a) })
f.bar.a = 2
f.bar.a = 3
f.bar.a = 4

输出:

代码语言:javascript
运行
复制
0
2
3
4

当然,使用KVO的缺点是您传入的关键路径必须是@objc dynamic,而键盘的根必须是NSObject子类。:(

我还没有尝试过,但是如果需要的话,应该可以将其扩展到多个关键路径上观察。

票数 2
EN

Stack Overflow用户

发布于 2020-06-05 11:20:52

您可以尝试使用CurrentValueSubject<Progress, Never>

代码语言:javascript
运行
复制
class SomeTask: ObservableObject {
    var progress = CurrentValueSubject<Progress, Never>(Progress(totalUnitCount: 5))

    func setProgress(_ value: Int) {
        progress.value.completedUnitCount = value
        progress.send(progress.value)
    }
}
代码语言:javascript
运行
复制
var task = SomeTask()
let cancellable = task.progress.sink { print($0.fractionCompleted) }
task.setProgress(3)
task.setProgress(1)

这样,您的Progress仍然可以是一个类。

票数 2
EN

Stack Overflow用户

发布于 2020-06-06 08:59:06

基于这些想法,我确实实现了一个@PublishedKVO属性包装器,并将它作为一个支持多个密钥路径的小型快捷包放在github上。

https://github.com/matis-schotte/PublishedKVO

可用于:

代码语言:javascript
运行
复制
class Example {
    @PublishedKVO(\.completedUnitCount)
    var progress = Progress(totalUnitCount: 2)

    @Published
    var textualRepresentation = "text"
}

let ex = Example()

// Set up the publishers
let c1 = ex.$progress.sink { print("\($0.fractionCompleted) completed") }
let c1 = ex.$textualRepresentation.sink { print("\($0)") }

// Interact with the class as usual
ex.progress.completedUnitCount += 1
// outputs "0.5 completed"

// And compare with Combines @Published (almost°) same behaviour
ex.textualRepresentation = "string"
// outputs "string"

ex.$progress.emit() // Re-emits the current value
ex.$progress.send(ex.progress) // Emits given value
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62212563

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档