@Published属性未从嵌套的视图模型更新视图 - SwiftUI

4

我不确定为什么当我有一个嵌套在另一个视图模型中的视图模型时,我的视图没有得到更新。我的理解是,在子视图模型中的@Published属性会触发父ViewModel的变化,从而导致其推送更改到UI。

这是子视图模型:

class FilterViewModel : ObservableObject, Identifiable {

    var id = UUID().uuidString
    var name  = ""
    var backgroundColour = ""
    @Published var selected = false

    private var cancellables = Set<AnyCancellable>()

    init(name: String){
        self.name = name
    
        $selected.map { _ in
            self.selected ? "Orange" : "LightGray"
        }
        .assign(to: \.backgroundColour, on: self)
        .store(in: &cancellables)
     }

    func changeSelected() {
    
        self.selected = !self.selected
    }
}

以下内容符合预期,点击按钮后背景颜色会发生变化。
struct ContentView: View {

    @ObservedObject var filterVM = FilterViewModel(name: "A")


    Button(action: { filterVM.changeSelected()}, label: {
        Text(filterVM.name)
            .background(Color(filterVM.backgroundColour))
    })
}

然而,我希望拥有一个过滤器视图模型的数组,因此尝试了以下代码:

class FilterListViewModel: ObservableObject {

    @Published var filtersVMS = [FilterViewModel]()

    init(){
        filtersVMS = [
            FilterViewModel(name: "A"),
            FilterViewModel(name: "B"),
            FilterViewModel(name: "C"),
            FilterViewModel(name: "D")
        ]
    }
}

然而,点击按钮后以下视图未更新。
struct ContentView: View {

    @ObservedObject var filterListVM = FilterListViewModel()

    Button(action: { filterListVM[0].changeSelected()}, label: {
        Text(filterListVM[0].name)
            .background(Color(filterListVM[0].backgroundColour))
    })
}
2个回答

5

另一种解决方案是为您的子模型使用单独的视图:

struct FilterView: View {
    @ObservedObject var filterVM: FilterViewModel

    var body: some View {
      Button(action: { filterVM.changeSelected()}, label: {
        Text(filterVM.name)
            .background(Color(filterVM.backgroundColour))
      })
    }
}

现在在父视图中,我们可以这样使用它:

struct ContentView: View {

    @ObservedObject var filterListVM = FilterListViewModel()

    // ...

    FilterView(filterVM: filterListVM[0])
}

谢谢,这个完美地解决了问题。你知道为什么这样行得通而不是通过它的父虚拟机引用子虚拟机吗? - Spdollaz
ObservedObject 只观察一级发布的值变化,在您的情况下,当内部子属性更改时,对子项的引用并未更改。 - Asperi

0
最简单的方法是将您的FilterViewModel定义为结构体。因此,它是一个值类型。当值发生变化时,结构体也会发生变化。然后,您的ListViewModel就会触发变化。
struct FilterViewModel : Identifiable {

    var id = UUID().uuidString
    var name  = ""
    var backgroundColour = ""
    var selected = false

    private var cancellables = Set<AnyCancellable>()

    init(name: String){
        self.name = name
    
        $selected.map { _ in
            self.selected ? "Orange" : "LightGray"
        }
        .assign(to: \.backgroundColour, on: self)
        .store(in: &cancellables)
     }

    mutating func changeSelected() {
    
        self.selected = !self.selected
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接