【问题标题】:@Published property not updating view from nested view model - SwiftUI@Published 属性未从嵌套视图模型更新视图 - SwiftUI
【发布时间】:2024-01-19 03:04:01
【问题描述】:

我不确定为什么当我有一个嵌套在另一个视图模型中时我的视图没有得到更新。我的理解是子视图模型中的 @Published 属性会触发父视图模型中的更改,从而将更改推送到 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))
    })
}

【问题讨论】:

    标签: swiftui


    【解决方案1】:

    替代解决方案是为您的子模型使用分离视图:

    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])
    }
    

    【讨论】:

    • 谢谢,这很好用。您知道为什么这样做而不是通过父虚拟机引用子虚拟机吗?
    【解决方案2】:

    最简单的方法是将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
        }
    }
    

    【讨论】: