当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive

Posted

技术标签:

【中文标题】当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive【英文标题】:onReceive not getting called in SwiftUI View when ObservedObject changes 【发布时间】:2020-02-27 18:42:12 【问题描述】:

每当来自 ObservedObject 的变量发生变化时,我都无法在 SwiftUI 视图中触发 onReceive 方法。

我尝试了两种方法:使用 @Publish 和使用 PassthroughSubject

这是视图模型

class MenuViewModel: ObservableObject 

@Published var selectedItems = Set<UUID>()
@Published var currentFocusItem: UUID?

// Output
let newItemOnFocus = PassthroughSubject<(UUID?), Never>()

// This function gets called good :)
func tapOnMenuItem(_ item: MenuItem) 
    if selectedItems.contains(item.id) 
        //These changes should trigger the onReceive?
        currentFocusItem = item.id  
        newItemOnFocus.send(item.id)
     else 
        selectedItems.insert(item.id)
        currentFocusItem = nil
        newItemOnFocus.send(nil)
    


Here is the View 试图捕捉 @Published var currentFocusItem

中的变化
struct MenuView: View 

    @ObservedObject private var viewModel: MenuViewModel
    @State var showPicker = false
    @State private var menu: Menu = Menu.mockMenu()


    init(viewModel: MenuViewModel = MenuViewModel()) 
        self.viewModel = viewModel
    

    var body: some View 
        VStack 
            List(menu.items, selection: $viewModel.selectedItems)  item in
                MenuItemView(item: item)
            

            Divider()
            getBottomView(showPicker: showPicker)
        

        .navigationBarTitle("Title")
        .navigationBarItems(trailing: Button(action: closeModal) 
            Image(systemName: "xmark")
        )
        .onReceive(viewModel.$currentFocusItem, perform:  itemUUID in
            self.showPicker = itemUUID != nil // <-- This only gets called at launch time
        )           
    

视图以相同的方式,但试图捕捉 PassthroughSubject

.onReceive(viewModel.newItemOnFocus, perform:  itemUUID in
            self.showPicker = itemUUID != nil // <-- This never gets called
        )

---------编辑----------

添加 MenuItemView,虽然 viewModel.tapOnMenuItem 总是被调用,所以我不确定它是否非常相关

MenuItemView 在这里:

struct MenuItemView: View 

    var item: MenuItem
    @ObservedObject private var viewModel: MenuViewModel = MenuViewModel()
    @State private var isSelected = false

    var body: some View 
        HStack(spacing: 24) 
            Text(isSelected ? " 1 " : item.icon)
                .font(.largeTitle)
                .foregroundColor(.blue)
                .bold()
            VStack(alignment: .leading, spacing: 12) 
                Text(item.name)
                    .bold()
                Text(item.description)
                    .font(.callout)
            
            Spacer()
            Text("\(item.points)\npoints")
                .multilineTextAlignment(.center)
        
        .padding()
        .onTapGesture 
            self.isSelected = true
            self.viewModel.tapOnMenuItem(self.item). // <-- Here tapOnMenuItem gets called
        
    

    func quantityText(isItemSelected: Bool) -> String 
        return isItemSelected ? "1" : item.icon
    

我做错了什么?

【问题讨论】:

请告诉我们MenuItem 以及tapOnMenuItem 的名称。 @Asperi 完成!它总是被正确调用,所以我认为这并不重要 【参考方案1】:

嗯,就是这样 - 你的 MenuViewMenuItemView 使用不同的视图模型实例

1)

struct MenuView: View 

    @ObservedObject private var viewModel: MenuViewModel
    @State var showPicker = false
    @State private var menu: Menu = Menu.mockMenu()


    init(viewModel: MenuViewModel = MenuViewModel())  // 1st one created

2)

struct MenuItemView: View 

    var item: MenuItem
    @ObservedObject private var viewModel: MenuViewModel = MenuViewModel() // 2nd one

因此,您修改了一个实例,但订阅了另一个实例的更改。就是这样。

解决方案:通过.environmentObject 或通过参数从MenuView 传递视图模型到MenuItemView

【讨论】:

天哪!我做了多么愚蠢的事情?‍♀️!非常感谢你是完全正确的!我将它作为参数传递,它现在可以工作了!

以上是关于当 ObservedObject 更改时,在 SwiftUI 视图中未调用 onReceive的主要内容,如果未能解决你的问题,请参考以下文章

@ObservedObject 更改后 SwiftUI 视图未更新

ObservedObject 更改时的通知

为啥我的@ObservedObject 没有在我的视图中执行更改?

嵌套 ObservedObject 中的更改不会更新 UI

当从另一个 ObservedObject 链接到 Published 属性时,视图不会对更改做出反应

SwiftUI ObservedObject 不更新父视图