在下面的代码(项目中某些代码的简化版本)中,我正在使用具有两个视图的MVVM模式:
在ViewModelA内部,我具有一个计算属性,该属性既可以避免View直接访问Model,又可以在更改模型内部的值(正在显示的值)时执行其他一些操作。
我还使用绑定将计算的值传递给ViewModelB,该绑定充当ViewB的StateObject。但是,拖动“滑块”以更改该值时,该值在ViewA上更改,但在ViewB上不变,并且滑块本身也不会滑动。正如预期的那样,在调试时,绑定内部的wrappedValue不会更改。
但是更改如何向上传播(我想是通过Binding的setter)而不是向下传播到ViewB?
我想只有在变量被复制到某个地方并且仅在一个地方更改时,这种情况才会发生,但是我似乎无法理解实际发生的位置或发生的情况。
提前谢谢!
观看次数:
import SwiftUI
struct ContentView: View {
@StateObject var viewModelA = ViewModelA()
var body: some View {
VStack{
ViewA(value: viewModelA.value)
ViewB(value: $viewModelA.value)
}
}
}
struct ViewA: View {
let value: Double
var body: some View {
Text("\(value)").padding()
}
}
struct ViewB: View {
@StateObject var viewModelB: ViewModelB
init(value: Binding<Double>){
_viewModelB = StateObject(wrappedValue: ViewModelB(value: value))
}
var body: some View {
VStack{
Text("\(viewModelB.value)")
Slider(value: $viewModelB.value, in: 0...1)
}
}
}
ViewModels
class ViewModelA: ObservableObject {
@Published var model = Model()
var value: Double {
get {
model.value
}
set {
model.value = newValue
// perform other checks and operations
}
}
}
class ViewModelB: ObservableObject {
@Binding var value: Double
init(value: Binding<Double>){
self._value = value
}
}
型号:
struct Model {
var value: Double = 0
}
答案 0 :(得分:1)
如果只看不能走的地方,您可能会想念下面的财富
打破真理的单一来源,并通过绑定共享来破坏@StateObject
的本地(私有)财产,这是您无法去的两个地方。
@EnvironmentObject
或更广泛地说,视图之间的“共享对象”的概念是下面的内容。
这是在没有MVVM废话的情况下执行此操作的示例:
import SwiftUI
final class EnvState: ObservableObject {@Published var value: Double = 0 }
struct ContentView: View {
@EnvironmentObject var eos: EnvState
var body: some View {
VStack{
ViewA()
ViewB()
}
}
}
struct ViewA: View {
@EnvironmentObject var eos: EnvState
var body: some View {
Text("\(eos.value)").padding()
}
}
struct ViewB: View {
@EnvironmentObject var eos: EnvState
var body: some View {
VStack{
Text("\(eos.value)")
Slider(value: $eos.value, in: 0...1)
}
}
}
这难道不是更容易阅读,更简洁,更不易出错,是否有更少的开销以及没有严重违反基本编码原理的情况吗?
MVVM不考虑值类型。 Swift引入值类型的原因是,您不必传递共享的可变引用,也不会创建各种错误。
然而,MVVM开发人员要做的第一件事是为每个视图引入共享的可变引用,并通过绑定传递引用...
现在您的问题:
我看到的唯一选择要么是每个模型仅使用一个ViewModel,要么必须通过绑定在ViewModels之间传递模型(或其属性)
另一种选择是删除MVVM,摆脱所有视图模型,而改用@EnvironmentObject
。
或者,如果您不想删除MVVM,请传递@ObservedObject
(您的视图模型是参考类型)而不是@Binding
。
例如;
struct ContentView: View {
@ObservedObject var viewModelA = ViewModelA()
var body: some View {
VStack{
ViewA(value: viewModelA)
ViewB(value: viewModelA)
}
}
}
在旁注中,“不要直接从视图访问模型”有什么意义?
当您的模型为值类型时,它具有零意义。
尤其是当您在聚会中像Cookie一样传递视图模型引用时,所有人都可以使用它。
答案 1 :(得分:0)
确实,它看起来像是破碎的单源或真理概念。取而代之的是,以下方法才有效(某事可能需要ViewModelB
,但在这种情况下可能不需要)
通过Xcode 12 / iOS 14测试
仅修改的部分:
struct ContentView: View {
@StateObject var viewModelA = ViewModelA()
var body: some View {
VStack{
ViewA(value: viewModelA.value)
ViewB(value: $viewModelA.model.value)
}
}
}
struct ViewB: View {
@Binding var value: Double
var body: some View {
VStack{
Text("\(value)")
Slider(value: $value, in: 0...1)
}
}
}