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

Posted

技术标签:

【中文标题】SwiftUI 如何将@Published 分配给另一个@Published【英文标题】:SwiftUI How to assign @Published to another @Published 【发布时间】:2020-10-24 04:20:46 【问题描述】:

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

如下:

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

我的代码在这里:

Test.Playground

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 时,FooGroupViewModel 中的selectedFoo 已更新,然后在MainViewModel 中应该得到更改(如@ObservedObject var fooGroupViewModel: FooGroupViewModel

我的问题是如何让currentFoo知道selectedFoofooGroupViewModel中的变化?我原以为这条线可以观察到selectedFoo 的变化,如果有任何更新则触发currentFoo 的变化并更新UI。

currentFoo = self.fooGroupViewModel.selectedFoo

但实际上它不起作用。

有什么帮助吗?谢谢!

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

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

这段代码使操场出错:

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

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

【问题讨论】:

【参考方案1】:

只做currentFoo = self.fooGroupViewModel.selectedFoo 肯定是行不通的——它只是将currentFoo 分配给当时selectedFoo 的任何值。

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

另外,请记住 @ObservedObject 仅在 View 中有意义,因此我将其删除。

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)

    

【讨论】:

非常感谢,刚刚将cancellables 更改为&amp;cancellables 完美!

以上是关于SwiftUI 如何将@Published 分配给另一个@Published的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 和具有 @Published 的组合 Observable 对象不断发布

如何将我的 SwiftUI 视图的 @State 交换为我的视图模型 @Published 变量?

如何检测 SwiftUI 中的 @Published 值发生了啥变化?

SwiftUI:如何使用 UserDefaults 持久化 @Published 变量?

在需要@Binding 的地方传递@Published(SwiftUI、Combine)

SwiftUI 如何知道将哪个 environmentObject 分配给哪个变量