MVVM 将数据从视图传递到另一个视图的 viewModel

MVVM Passing data from view to another view's viewModel

我是 MVVM 的新手,我正在尝试将位置数据从我的 ContenView 传递到 DetailsView 的 viewModel,即 DetailsViewViewModel。

我的打开视图 -> ContentView(我的数据在这里)

第二视图 -> DetailsView

必须到达数据 -> DetailsViewViewModel

这是我在 ContentView

中的 sheet
.sheet(item: $viewModel.selectedPlace) { place in
            DetailsView(location: place) { newLocation in
                viewModel.updateLocation(location: newLocation)
            }

我知道我正在尝试将我的数据发送到详细信息视图,但这是错误的。在我将架构转换为 MVVM 之前就像那样,这是我唯一无法转换的地方。 这也是我的 DetailsViewViewModel

extension DetailsView {
@MainActor class DetailsViewViewModel: ObservableObject {
    enum LoadingState {
        case loading, loaded, failed
    }
    var location: Location
    @Published var name: String
    @Published var description: String
    @Published var loadingState = LoadingState.loading
    @Published var pages = [Page]()
    init() {
        self.location =  // ??? how should i initialize?
        self.name = location.name
        self.description = location.description
    }

正确的方法是什么。在另一个视图视图模型中使用另一个视图数据。

@StateObject var viewModel = ViewModel()

struct ParentView: View {
    
    var body: some View {
        Button(action: {
            
        }, label: {
            Text("btn")
        })
            .sheet(item: $viewModel.selectedPlace) { place in
                DetailView(name: place.name,
                           location: place.location,
                           description: place.description)
            }
    }
}

struct DetailView: View {
    
    var name: String
    var location: String
    var description: String
    
    var body: some View {
        VStack {
            Text(name)
            Text(location)
            Text(description)
        }
    }
}

由于位置数据是您的业务层数据,您需要 use-case 将其提供给两个视图模型,优化它缓存响应是可行的方法。

-ViewModel 负责保存最新的视图状态和数据

-领域层负责处理业务逻辑

-数据层(网络、缓存、持久性或in-memory)负责提供最高效的数据storage/retrieval解决方案

因此,如果您接受这些定义并考虑为这些视图模型编写测试,您就会知道从另一个 ViewModel 注入数据是不正确的,因为您不会测试该视图模型以确保它通过数据到下一个 viewModel,这不是它的责任,但你为你的数据层编写了许多测试,以确保服务调用和缓存系统正常工作。

让我尝试举一个使用 @EnvironmentObject:

的例子
  1. 您的视图模型是一个 class,符合 ObservableObject,因此您可以使用那些很好的变量 @Published 来改变视图的状态。

  2. 您的主视图 - 或者您的 App - 必须“拥有”视图模型,这意味着它需要创建您的视图模型的唯一实例,供所有观看次数。

  3. 您使用 @StateObject@ObservableObject 将视图模型从一个视图传递到另一个视图,但在本例中我更喜欢使用另一种方法。让您的主视图在环境中注入您的视图模型的 实例 ,因此所有其他视图将从中读取。主视图使用 .environmentObject(viewModel) 来做到这一点。

  4. 其他视图通过调用@EnvironmentObject从环境中读取视图模型。他们创建了一个变量,仅指定 类型 - 环境中每种类型只能有一个实例。

这是所有视图从同一模型读取的方式。请参阅下面的功能示例:

第 1 步:

class MyViewModel: ObservableObject {
    
    @Published private(set) var color: Color = .blue
    
    @Published var showSheet = false
    
    func changeColorTo(_ color: Color) {
        self.color  = color
    }
    
}

第 2 步和第 3 步:

struct Example: View {
    
    @StateObject private var viewModel = MyViewModel()   // Here is the step (2)
    
    var body: some View {
        OneView()
            .environmentObject(viewModel)    // Here is the step (3)
    }
}

步骤 4 在两个不同的视图中:

struct OneView: View {
    @EnvironmentObject var viewModel: MyViewModel    // Here is step (4)
    
    var body: some View {
        VStack {
            Text("Current color")
                .padding()
                .background(viewModel.color)
            
            Button {
                if viewModel.color == .blue {
                    viewModel.changeColorTo(.yellow)
                } else {
                    viewModel.changeColorTo(.blue)
                }
            } label: {
                Text("Change color")
            }

            Button {
                viewModel.showSheet.toggle()
            } label: {
                Text("Now, show a sheet")
            }
            .padding()
        }
        
        .sheet(isPresented: $viewModel.showSheet) {
            DetailView()
        }
    }
}

struct DetailView: View {
    @EnvironmentObject var viewModel: MyViewModel    // Here is step (4)
    
    var body: some View {
        VStack {
            Text("The sheet is showing")
                .padding()
            
            Button {
                viewModel.showSheet.toggle()
            } label: {
                Text("Now, stop showing the sheet")
            }
        }
    }
}

当您添加如下所示的 DetailsView 时,您需要从 ContentView sheet 初始化 DetailsViewModel:

ContentView

struct ContentView: View {
    @StateObject var vm = ViewModel()
    var body: some View {
        Text("Hello, world!")
            .sheet(item: $vm.selectedPlace,
                   onDismiss: didDismiss) {newLocation in
                //Here Initialise the DetailViewModel with a location
                DetailsView(detailsVM: DetailsViewModel(location: newLocation))
            }

    }
    
    func didDismiss(){
        
    }
}

详细信息视图:

struct DetailsView: View {
    @StateObject var detailsVM : DetailsViewModel
    var body: some View {
        Text("This is the DetailesView")
    }
}

DetailsViewModel:

class DetailsViewModel:ObservableObject{
    @Published var location:Location
    init(location:Location){
        self.location = location
    }
}