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

时间:2020-10-24 04:20:46

标签: ios swift swiftui combine

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

像下面这样:

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

enter image description here

我的代码在这里:

测试场

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())

MainViewModel中,如果currentFoo有所更改,则应该更新UI。

单击foo1foo2时,selectedFoo中的FooGroupViewModel已更新,则MainViewModel中的@ObservedObject var fooGroupViewModel: FooGroupViewModel应该得到更改(如currentFoo

我的问题是如何让selectedFoo知道fooGroupViewModelselectedFoo的变化?我以为这行代码可以观察currentFoo的更改,如果有更新,则触发currentFoo = self.fooGroupViewModel.selectedFoo 更改并更新UI。

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

但是实际上不起作用。

有帮助吗?谢谢!

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

// 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.

此代码使游乐场出错:

const path = require("path");

module.exports = {
    mode: "none",
    entry: ["core-js/stable", "./src/js/index.js"],
    output: {
        path: path.resolve(__dirname + "/dist"),
        filename: "bundle.js",
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [[
                            "@babel/preset-env",{
                                "useBuiltIns": "usage",
                                "corejs" : {
                                    version : "3",
                                    proposals : true
                                },
                                "targets" : {
                                    "browsers": ["last 3 versions", "ie >= 11"],
                                    "node": "current"
                                }
                            }
                        ]],
                        plugins: ["@babel/transform-runtime"]
                    }
                }
            }
        ]
    },
    // devtool: "eval-source-map"
}

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

1 个答案:

答案 0 :(得分:1)

仅仅做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)

    }
}