我有一个父 View 和一个 subview ,每个 View 都有自己的 viewModel。父 View 注入(inject) subview 的 viewModel。
父 View 无法正确响应 subview 的计算属性 isFormInvalid
的更改( subview 可以)。
@Published 无法添加到计算属性中,并且我在该区域看到的其他问题/答案并没有像这个问题那样专注于拥有单独的 viewModel。我希望单独的 viewModel 来提高可测试性,因为 subview 可能会变得相当复杂的形式。
这是一个可以最小化重现该问题的文件:
import SwiftUI
extension ParentView {
final class ViewModel: ObservableObject {
@ObservedObject var childViewViewModel: ChildView.ViewModel
init(childViewViewModel: ChildView.ViewModel = ChildView.ViewModel()) {
self.childViewViewModel = childViewViewModel
}
}
}
struct ParentView: View {
@ObservedObject private var viewModel: ViewModel
init(viewModel: ViewModel = ViewModel()) {
self.viewModel = viewModel
}
var body: some View {
ChildView(viewModel: viewModel.childViewViewModel)
.navigationBarTitle("Form", displayMode: .inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
addButton
}
}
}
private var addButton: some View {
Button {
print("======")
print(viewModel.childViewViewModel.$name)
} label: {
Text("ParentIsValid?")
}
.disabled(viewModel.childViewViewModel.isFormInvalid) // FIXME: doesn't work, but the actual fields work in terms of two way updating
}
}
struct ParentView_Previews: PreviewProvider {
static var previews: some View {
let childVm = ChildView.ViewModel()
let vm = ParentView.ViewModel(childViewViewModel: childVm)
NavigationView {
ParentView(viewModel: vm)
}
}
}
// MARK: child view
extension ChildView {
final class ViewModel: ObservableObject {
// MARK: - public properties
@Published var name = ""
var isFormInvalid: Bool {
print("isFormInvalid")
return name.isEmpty
}
}
}
struct ChildView: View {
@ObservedObject private var viewModel: ViewModel
init(viewModel: ViewModel = ViewModel()) {
self.viewModel = viewModel
}
var body: some View {
Form {
Section(header: Text("Name")) {
nameTextField
}
Button {} label: {
Text("ChildIsValid?: \(String(!viewModel.isFormInvalid))")
}
.disabled(viewModel.isFormInvalid)
}
}
private var nameTextField: some View {
TextField("Add name", text: $viewModel.name)
.autocapitalization(.words)
}
}
struct ChildView_Previews: PreviewProvider {
static var previews: some View {
let vm = ChildView.ViewModel()
ChildView(viewModel: vm).preferredColorScheme(.light)
}
}
谢谢!
最佳答案
计算属性不会触发任何更新。正是对 @Published
属性的更改触发了更新,当发生这种情况时,计算属性将被重新评估。这按预期工作,您可以在 ChildView
中看到。您面临的问题是 ObservableObject 并不是真正为链接而设计的(更新到子级不会触发对父级的更新。您可以通过从子级重新发布更新来解决这个问题:您已经订阅到子级的 objectWillChange
,每次发出时都会手动触发父级的 objectWillChange
。
extension ParentView {
final class ViewModel: ObservableObject {
@ObservedObject var childViewViewModel: ChildView.ViewModel
private var cancellables = Set<AnyCancellable>()
init(childViewViewModel: ChildView.ViewModel = ChildView.ViewModel()) {
self.childViewViewModel = childViewViewModel
childViewViewModel
.objectWillChange
.receive(on: RunLoop.main)
.sink { [weak self] _ in
self?.objectWillChange.send()
}
.store(in: &cancellables)
}
}
}
关于ios - subview 模型的计算属性不会更新父 View 模型的 @ObservedObject,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69778621/