我有一个可以观察到的AppState:
class AppState: ObservableObject {
private init() {}
static let shared = AppState()
@Published fileprivate(set) var isLoggedIn = false
}
视图模型应根据状态(isLoggedIn
)决定显示哪个视图:
class HostViewModel: ObservableObject, Identifiable {
enum DisplayableContent {
case welcome
case navigationWrapper
}
@Published var containedView: DisplayableContent = AppState.shared.isLoggedIn ? .navigationWrapper : .welcome
}
最后,HostView
会观察containedView
属性,并根据该属性显示正确的视图。
我的问题是,上面的代码未观察到isLoggedIn
,而且我似乎也找不到解决方法。我非常确定这是一种简单的方法,但是经过4小时的反复试验,我希望这里的社区能够为我提供帮助。
答案 0 :(得分:2)
将@minMonthlyPrice
添加到视图时,SwiftUI将为ObservedObject
发布者添加一个接收者,您需要执行相同的操作。由于objectWillChange
是在objectWillChange
更改之前发送的,因此最好添加一个发送其isLoggedIn
的发布者。当您对初始值和更改感兴趣时,didSet
可能是最好的。然后,您需要在CurrentValueSubject<Bool, Never>
中订阅HostViewModel
的新发布者,并使用发布的值更新AppState
。使用containedView
可能会导致参考周期,因此对assign
的引用较弱的sink
最好。
没有代码,但是非常简单。最后要注意的陷阱是将返回的值从self
保存到sink
,否则您的订户将消失。
答案 1 :(得分:1)
免责声明:
这不是解决问题的完整方法,它不会触发
objectWillChange
,因此对ObservableObject
毫无用处。但这可能对某些相关问题很有用。
主要思想是创建propertyWrapper
,该属性将根据链接的Publisher
中的更改来更新属性值:
@propertyWrapper
class Subscribed<Value, P: Publisher>: ObservableObject where P.Output == Value, P.Failure == Never {
private var watcher: AnyCancellable?
init(wrappedValue value: Value, _ publisher: P) {
self.wrappedValue = value
watcher = publisher.assign(to: \.wrappedValue, on: self)
}
@Published
private(set) var wrappedValue: Value {
willSet {
objectWillChange.send()
}
}
private(set) lazy var projectedValue = self.$wrappedValue
}
用法:
class HostViewModel: ObservableObject, Identifiable {
enum DisplayableContent {
case welcome
case navigationWrapper
}
@Subscribed(AppState.shared.$isLoggedIn.map({ $0 ? DisplayableContent.navigationWrapper : .welcome }))
var contained: DisplayableContent = .welcome
// each time `AppState.shared.isLoggedIn` changes, `contained` will change it's value
// and there's no other way to change the value of `contained`
}
答案 2 :(得分:1)
工作解决方案:
在使用Combine的两个星期之后,我现在又重新处理了以前的解决方案(请参阅编辑历史记录),这是我现在能想到的最好的解决方案。这仍然不完全是我的初衷,因为contained
既不是订阅者又是发布者,但是我认为始终需要AnyCancellable
。如果有人知道实现我的愿景的方法,请让我知道。
class HostViewModel: ObservableObject, Identifiable {
@Published var contained: DisplayableContent
private var containedUpdater: AnyCancellable?
init() {
self.contained = .welcome
setupPipelines()
}
private func setupPipelines() {
self.containedUpdater = AppState.shared.$isLoggedIn
.map { $0 ? DisplayableContent.mainContent : .welcome }
.assign(to: \.contained, on: self)
}
}
extension HostViewModel {
enum DisplayableContent {
case welcome
case mainContent
}
}
答案 3 :(得分:1)
订阅嵌入式@Published
中ObservedObject
变量的更改的通用解决方案是将objectWillChange
通知传递给父对象。
示例:
import Combine
class Parent: ObservableObject {
@Published
var child = Child()
var sink: AnyCancellable?
init() {
sink = child.objectWillChange.sink(receiveValue: objectWillChange.send)
}
}
class Child: ObservableObject {
@Published
var counter: Int = 0
func increase() {
counter += 1
}
}
与SwiftUI一起使用演示:
struct ContentView: View {
@ObservedObject
var parent = Parent()
var body: some View {
VStack(spacing: 50) {
Text( "\(parent.child.counter)")
Button( action: parent.child.increase) {
Text( "Increase")
}
}
}
}