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
:
的例子
您的视图模型是一个 class,符合 ObservableObject
,因此您可以使用那些很好的变量 @Published
来改变视图的状态。
您的主视图 - 或者您的 App
- 必须“拥有”视图模型,这意味着它需要创建您的视图模型的唯一实例,供所有观看次数。
您使用 @StateObject
和 @ObservableObject
将视图模型从一个视图传递到另一个视图,但在本例中我更喜欢使用另一种方法。让您的主视图在环境中注入您的视图模型的 实例 ,因此所有其他视图将从中读取。主视图使用 .environmentObject(viewModel)
来做到这一点。
其他视图通过调用@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
}
}
我是 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
:
您的视图模型是一个 class,符合
ObservableObject
,因此您可以使用那些很好的变量@Published
来改变视图的状态。您的主视图 - 或者您的
App
- 必须“拥有”视图模型,这意味着它需要创建您的视图模型的唯一实例,供所有观看次数。您使用
@StateObject
和@ObservableObject
将视图模型从一个视图传递到另一个视图,但在本例中我更喜欢使用另一种方法。让您的主视图在环境中注入您的视图模型的 实例 ,因此所有其他视图将从中读取。主视图使用.environmentObject(viewModel)
来做到这一点。其他视图通过调用
@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
}
}