SwiftUI 如何将@Published 分配给另一个@Published

Wil*_* Hu 1 ios swift swiftui combine

我有一个视图(MainView),里面有另一个视图(FooGroupView)。每次我单击 中的项目,FooGroupView然后将值更改传递给MainView.

就像下面这样:

白色的视图是MainView,红色的是FooGroupView。如果单击foo1则文本应该Current:foo1,然后如果单击foo2Current:foo2

在此输入图像描述

我的代码在这里:

测试游乐场

import SwiftUI
import PlaygroundSupport

struct FooRowView: View {

    var foo: Foo

    var body: some View {
        VStack {
            Color(.red)
            Text(foo.name)
        }
    }
}

class Foo: Identifiable, ObservableObject {
    var name: String
    var id: String

    init(name: String, id: String) {
        self.name = name
        self.id = id
    }
}

final class FooGroupViewModel: ObservableObject {

    var foos: [Foo] {
        didSet {
            self.selectedFoo = foos.first
        }
    }

    @Published var selectedFoo: Foo? {
        didSet {
            print("You selected: \(selectedFoo?.name)")
        }
    }

    init(foos: [Foo] = []) {
        self.foos = foos
        self.selectedFoo = foos.first
    }
}

struct FooGroupView: View {

    var viewModel: FooGroupViewModel

    var body: some View {
        ScrollView(.horizontal) {
            HStack(alignment: .center, spacing: 32, content: {

                // Error: error: Execution was interrupted, reason: signal SIGABRT.
                //The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

                /*ForEach(viewModel.foos) { foo in
                    Text(foo.name)
                }*/


                Text(viewModel.foos[0].name).onTapGesture {
                    viewModel.selectedFoo = viewModel.foos[0]
                }
                Text(viewModel.foos[1].name).onTapGesture {
                    viewModel.selectedFoo = viewModel.foos[1]
                }
            })
        }
    }
}

final class MainViewModel: ObservableObject {
    @ObservedObject var fooGroupViewModel: FooGroupViewModel

    @Published var currentFoo: Foo?

    init() {
        fooGroupViewModel = FooGroupViewModel(foos: [Foo(name: "foo1", id: "1"), Foo(name: "foo2", id: "2")])
        currentFoo = self.fooGroupViewModel.selectedFoo

    }
}

struct MainView: View {

    @ObservedObject var viewModel = MainViewModel()

    var body: some View {
        VStack {
            FooGroupView(viewModel: viewModel.fooGroupViewModel)
                .background(Color.red)
            Spacer()
            Text("Current:\(viewModel.currentFoo!.name)")
        }.frame(width: 200, height: 200)

    }
}


PlaygroundPage.current.liveView = UIHostingController(rootView: MainView())
Run Code Online (Sandbox Code Playgroud)

如果MainViewModel发生currentFoo变化,则应更新 UI。

当单击foo1或 时foo2selectedFooinFooGroupViewModel已更新,则 inMainViewModel应该得到更改(如@ObservedObject var fooGroupViewModel: FooGroupViewModel

我的问题是如何让知道incurrentFoo的变化?我认为这一行可以观察 的变化,如果有任何更新,则触发更改并更新 UI。selectedFoofooGroupViewModelselectedFoocurrentFoo

currentFoo = self.fooGroupViewModel.selectedFoo
Run Code Online (Sandbox Code Playgroud)

但实际上是行不通的。

有什么帮助吗?谢谢!

我还有另一个问题,它是上面代码中的注释(如果将我的代码粘贴到 Playground 中,则为第 56 行)

ForEach(viewModel.foos) { foo in
                    Text(foo.name)
                }
Run Code Online (Sandbox Code Playgroud)

这段代码使操场错误:

// Error: error: Execution was interrupted, reason: signal SIGABRT.
                //The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
Run Code Online (Sandbox Code Playgroud)

控制台中没有任何其他信息。不知道为什么。谢谢你的帮助。

New*_*Dev 9

仅仅这样做currentFoo = self.fooGroupViewModel.selectedFoo肯定是行不通的——它只是分配给当时的currentFoo任何东西。selectedFoo

通过您的设置,您需要订阅 中的更改fooGroupViewModel.selectedFoo,这可以通过发布者来完成Published,发布者可用,因为它是一个@Published属性。

另外,请记住,这只@ObservedObject在视图中才有意义,所以我将其删除。

import Combine

// ...
 
final class MainViewModel: ObservableObject {

    var fooGroupViewModel: FooGroupViewModel
    @Published var currentFoo: Foo?

    private var cancellables: Set<AnyCancellable> = []

    init() {
        fooGroupViewModel = FooGroupViewModel(foos: 
                     [Foo(name: "foo1", id: "1"), Foo(name: "foo2", id: "2")])
        
        fooGroupViewModel.$selectedFoo
           .assign(to: \.currentFoo, on: self)
           .store(in: &cancellables)

    }
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢,只需将“cancellables”更改为“&amp;cancellables”即可完美! (2认同)