在下面的代码(项目中某些代码的精简版本)中,我使用具有两个 View 的 MVVM 模式:
- ViewA - 显示存储在 ObservableObject ViewModel 中的值;
- ViewB - 显示相同的值,并有一个更改该值的 slider ,该值使用 Binding 传递到 View 。
在 ViewModelA 内部,我有一个计算属性,它既可以避免 View 直接访问模型,又可以在模型内部的值(正在显示的值)更改时执行一些其他操作。
我还使用 Binding 将该计算值传递给 ViewModelB,该值充当 ViewB 的 StateObject。但是,当拖动 slider 更改该值时,ViewA 上的值会更改,但 ViewB 上的值不会更改,并且 slider 本身不会滑动。正如预期的那样,调试时,Binding 内的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)
}
}
}
View 模型:
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
}
最佳答案
If you only look where you can't go, you might just miss the riches below
打破单一事实来源,以及通过绑定(bind)共享来破坏 @StateObject
的本地(私有(private))属性是两个你不能去的地方。
@EnvironmentObject
或更一般地说, View 之间“共享对象”的概念是下面的财富。
这是一个没有 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 开发人员要做的第一件事就是为每个 View 引入共享可变引用,并通过绑定(bind)传递引用...
现在回答你的问题:
the only options I see are either using only one ViewModel per Model, or having to pass the Model (or it's properties) between ViewModels through Binding
另一个选择是放弃 MVVM,摆脱所有 View 模型,并使用 @EnvironmentObject
代替。
或者,如果您不想删除 MVVM,请传递 @ObservedObject
(您的 View 模型是引用类型)而不是 @Binding
。
例如;
struct ContentView: View {
@ObservedObject var viewModelA = ViewModelA()
var body: some View {
VStack{
ViewA(value: viewModelA)
ViewB(value: viewModelA)
}
}
}
顺便说一句,“不要直接从 View 访问模型”有什么意义?
当您的模型是值类型时,它毫无意义。
特别是当您像聚会中的 cookie 一样传递 View 模型引用时,每个人都可以拥有它。
关于mvvm - SwiftUI - @Binding 到访问 ObservableObject 属性内的值的计算属性会重复变量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62935978/