SwiftUI和Combine noob在这里,我在操场上隔离了我遇到的问题。这是操场。

final class ReactiveContainer<T: Equatable> {
    @Published var containedValue: T?
}

class AppContainer {
    static let shared = AppContainer()

    let text = ReactiveContainer<String>()
}

struct TestSwiftUIView: View {

    @State private var viewModel = "test"

    var body: some View {
        Text("\(viewModel)")
    }

    init(textContainer: ReactiveContainer<String>) {

        textContainer.$containedValue.compactMap {
            print("compact map \($0)")
            return $0
        }.assign(to: \.viewModel, on: self)
    }
}

AppContainer.shared.text.containedValue = "init"


var testView = TestSwiftUIView(textContainer: AppContainer.shared.text)
print(testView)

print("Executing network request")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
    AppContainer.shared.text.containedValue = "Hello world"
    print(testView)
}

我在操场上跑的时候是这样的:
compact map Optional("init")
TestSwiftUIView(_viewModel: SwiftUI.State<Swift.String>(_value: "test", _location: nil))
Executing network request
TestSwiftUIView(_viewModel: SwiftUI.State<Swift.String>(_value: "test", _location: nil))

如您所见,那里有两个问题:
  • 精简 map 关闭仅在订阅时调用一次,但在运行分派时不调用
  • 从不将分配运算符称为

  • 在过去的几个小时中,我一直试图解决此问题,但没有成功。也许某人在SwiftUI / Combine中有最高级的知识可以帮助我,谢谢!

    编辑

    这是有效的解决方案:

    struct ContentView: View {
    
        @State private var viewModel = "test"
        let textContainer: ReactiveContainer<String>
    
        var body: some View {
            Text(viewModel).onReceive(textContainer.$containedValue) { (newContainedValue) in
                self.viewModel = newContainedValue ?? ""
            }
        }
    
        init(textContainer: ReactiveContainer<String>) {
            self.textContainer = textContainer
        }
    }
    

    最佳答案

    我宁愿使用下面的ObservableObject/ObservedObject模式,但也可以使用其他变体(进一步提供)

    所有均通过Xcode 11.2 / iOS 13.2测试

    final class ReactiveContainer<T: Equatable>: ObservableObject {
        @Published var containedValue: T?
    }
    
    struct TestSwiftUIView: View {
    
        @ObservedObject var vm: ReactiveContainer<String>
    
        var body: some View {
            Text("\(vm.containedValue ?? "<none>")")
        }
    
        init(textContainer: ReactiveContainer<String>) {
            self._vm = ObservedObject(initialValue: textContainer)
        }
    }
    

    备用:

    以下内容可解决您的问题(如果您不存储订户,发布者将立即被取消)
    private var subscriber: AnyCancellable?
    init(textContainer: ReactiveContainer<String>) {
    
        subscriber = textContainer.$containedValue.compactMap {
            print("compact map \($0)")
            return $0
        }.assign(to: \.viewModel, on: self)
    }
    

    请注意,视图的状态仅在视图层次结构中链接,就像在Playground中一样,它仅保留初始值。

    另一种可能更适合SwiftUI层次结构的方法是
    struct TestSwiftUIView: View {
    
        @State private var viewModel: String = "test"
    
        var body: some View {
            Text("\(viewModel)")
                .onReceive(publisher) { value in
                    self.viewModel = value
                }
        }
    
        let publisher: AnyPublisher<String, Never>
        init(textContainer: ReactiveContainer<String>) {
    
            publisher = textContainer.$containedValue.compactMap {
                print("compact map \($0)")
                return $0
            }.eraseToAnyPublisher()
        }
    }
    

    关于ios - @Published和.assign对值更新没有反应,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60667976/

    10-13 03:57