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

发布于 2025-01-09 12:18:11 字数 1095 浏览 0 评论 0原文

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

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

第二个视图 ->详情查看

数据必须到达-> DetailsViewViewModel

这是我在 ContentView 中的工作表,

.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
    }

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

I'm new to MVVM and i am trying to pass a location data from my ContenView to DetailsView's viewModel which is DetailsViewViewModel.

My Opening View -> ContentView (My data is here)

Second View -> DetailsView

Data must be reach -> DetailsViewViewModel

Here is my sheet in ContentView

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

I know i'm trying to send my data to details view and it's wrong. It was like that before i convert the architecture to the MVVM and this is the only place that i couldn't convert.
Also here is my 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
    }

What is the proper way to this. Using another views data in another views viewmodel.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一直在等你来 2025-01-16 12:18:11

让我尝试举一个使用 @EnvironmentObject 便利性的示例:

  1. 您的视图模型是一个符合 ObservableObject 的类,因此您可以拥有那些不错的改变视图状态的变量@Published

  2. 您的主视图 - 或者您的App - 必须“拥有”视图模型,这意味着它需要创建将由所有视图使用的视图模型的唯一实例。< /p>

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

Let me try to put in an example that uses the convenience of @EnvironmentObject:

  1. Your view model is a class that conforms to ObservableObject, so you can have those nice variables @Published that change the state of the views.

  2. Your main view - or also your App - must "own" the view model, meaning it needs to create the one and only instance of your view model that will be used by all views.

  3. You pass the view model from one view to another using @StateObject and @ObservableObject, but in this example I prefer to use another approach. Make your main view inject the instance of your view model in the environment, so all other views will read from that. The main view uses .environmentObject(viewModel) to do that.

  4. The other views read the view model from the environment by calling @EnvironmentObject. They create a variable specifying only the type - there can only be one instance per type in the environment.

This is the way with which all view will read from the same model. See below a functioning example:

Step 1:

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

Steps 2 and 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)
    }
}

Step 4 in two different views:

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")
            }
        }
    }
}
画▽骨i 2025-01-16 12:18:11

由于位置数据是您的业务层数据,因此您需要一个用例将其提供给两个视图模型,并且要优化它,缓存响应是可行的方法。

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

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

-数据层(网络、缓存、持久化或内存)负责提供最高效的数据存储/检索解决方案

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

since location data is your business layer data, you need a use-case to provide it to both view models, and to optimize it caching the response is the way to go.

-ViewModel is responsible to hold the latest view states and data

-The domain layer is responsible to handle business logic

-The data layer (networking, cache, persistence, or in-memory) is responsible for providing the most efficient data storage/retrieval solutions

So, if you are okay with these defenitions and think of writing test for these view models you know that it is not right to inject data from another ViewModel because you would not test that view model on making sure it passes the data to the next viewModel and it is not its responsibility, but you write many tests for you data layer to make sure service calls and caching systems are working properly.

愿与i 2025-01-16 12:18:11

当您添加DetailsView时,您需要从ContentView表初始化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(){
        
    }
}

DetailsView:

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

You need to initialise DetailsViewModel from ContentView sheet when you are adding the DetailsView like below:

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(){
        
    }
}

DetailsView:

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
    }
}
感性不性感 2025-01-16 12:18:11
@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)
        }
    }
}
@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)
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文