SwiftUI 中的 MVVM 模型
MVVM model in SwiftUI
我想根据 MVVM 将视图与视图模型分开。我将如何在 SwiftUI 中创建模型?我读到应该使用 struct 而不是 class.
例如,我有一个公园模型,您可以在其中植树:
// View Model
struct Park {
var numberOfTrees = 0
func plantTree() {
numberOfTrees += 1 // Cannot assign to property: 'self' is immutable
}
}
// View
struct ParkView: View {
var park: Park
var body: some View {
// …
}
}
阅读有关 @State
的内容,使结构有些可变,所以我尝试了:
struct Park {
@State var numberOfTrees = 0 // Enum 'State' cannot be used as an attribute
func plantTree() {
numberOfTrees += 1 // Cannot assign to property: 'self' is immutable
}
}
我确实直接在View
中成功使用了@State
。不过,这对分离视图模型代码没有帮助。
我可以使用 class
:
class Park: ObservableObject {
var numberOfTrees = 0
func plantTree() {
numberOfTrees += 1
}
}
…但是我会在使用嵌套在另一个视图模型中的视图模型时遇到麻烦,比如 City
:
struct City {
@ObservedObject var centerPark: Park
}
centerPark
中的更改不会发布,因为 Park
现在是引用类型(至少在我的测试或 here 中不是)。另外,我想知道你是如何使用 struct
.
解决这个问题的
作为起点:
// Model
struct Park {
var numberOfTrees = 0
mutating func plantTree() { // `mutating`gets rid of your error
numberOfTrees += 1
}
}
// View Model
class CityVM: ObservableObject {
@Published var park = Park() // creates a Park and publishes it to the views
// ... other @Published things ...
// Intents:
func plantTree() {
park.plantTree()
}
}
// View
struct ParkView: View {
// create the ViewModel, which creates the model(s)
// usually you would do this in the App struct and make available to all views by .environmentObject
@StateObject var city = CityVM()
var body: some View {
VStack {
Text("My city has \(city.park.numberOfTrees) trees.")
Button("Plant one more") {
city.plantTree()
}
}
}
}
mutating func
是解决方法,但我想我会在下面包含一些其他信息:
我们不将 MVVM 与 SwiftUI 一起使用,因为我们不将 类 用于瞬态视图状态,并且我们不在 MVVM/MVC 意义上控制视图。 SwiftUI 自动为我们创建和更新屏幕上的视图,即 UILabels、UITableView 等。 SwiftUI View 结构本质上已经是视图模型,因此如果您要将其重新创建为一个对象,您不仅会不必要地使您的代码更加复杂,而且还会引入 SwiftUI 试图通过使用结构消除的对象引用错误。使用像 @State
和 @Binding
这样的 属性 包装器,SwiftUI 正在做一些神奇的事情来使结构表现得像一个对象,忽略它不是一个好主意。为了使您的 View 结构更易于测试,您可以将相关的变量提取到一个结构中并使用像这样的变异函数:
// View Model
struct ParkConfig {
var numberOfTrees = 0
mutating func plantTree() {
numberOfTrees += 1
}
}
struct ContentView {
@State var parkConfig = ParkConfig()
var body: some View {
ParkView(config: $parkConfig)
}
}
// View
struct ParkView: View {
@Binding var config: ParkConfig
var body: some View {
Button("Click Me") {
config.plantTree()
}
}
}
您可以看到 Apple 在 Data Essentials in SwiftUI WWDC 2020 at 4:18 中展示了这种模式,他说“EditorConfig 可以在其属性上保持不变量并独立测试。而且因为 EditorConfig 是一个值类型,任何更改属性 EditorConfig,就像它的进度一样,是对 EditorConfig 本身的更改。"
我想根据 MVVM 将视图与视图模型分开。我将如何在 SwiftUI 中创建模型?我读到应该使用 struct 而不是 class.
例如,我有一个公园模型,您可以在其中植树:
// View Model
struct Park {
var numberOfTrees = 0
func plantTree() {
numberOfTrees += 1 // Cannot assign to property: 'self' is immutable
}
}
// View
struct ParkView: View {
var park: Park
var body: some View {
// …
}
}
阅读有关 @State
的内容,使结构有些可变,所以我尝试了:
struct Park {
@State var numberOfTrees = 0 // Enum 'State' cannot be used as an attribute
func plantTree() {
numberOfTrees += 1 // Cannot assign to property: 'self' is immutable
}
}
我确实直接在View
中成功使用了@State
。不过,这对分离视图模型代码没有帮助。
我可以使用 class
:
class Park: ObservableObject {
var numberOfTrees = 0
func plantTree() {
numberOfTrees += 1
}
}
…但是我会在使用嵌套在另一个视图模型中的视图模型时遇到麻烦,比如 City
:
struct City {
@ObservedObject var centerPark: Park
}
centerPark
中的更改不会发布,因为 Park
现在是引用类型(至少在我的测试或 here 中不是)。另外,我想知道你是如何使用 struct
.
作为起点:
// Model
struct Park {
var numberOfTrees = 0
mutating func plantTree() { // `mutating`gets rid of your error
numberOfTrees += 1
}
}
// View Model
class CityVM: ObservableObject {
@Published var park = Park() // creates a Park and publishes it to the views
// ... other @Published things ...
// Intents:
func plantTree() {
park.plantTree()
}
}
// View
struct ParkView: View {
// create the ViewModel, which creates the model(s)
// usually you would do this in the App struct and make available to all views by .environmentObject
@StateObject var city = CityVM()
var body: some View {
VStack {
Text("My city has \(city.park.numberOfTrees) trees.")
Button("Plant one more") {
city.plantTree()
}
}
}
}
mutating func
是解决方法,但我想我会在下面包含一些其他信息:
我们不将 MVVM 与 SwiftUI 一起使用,因为我们不将 类 用于瞬态视图状态,并且我们不在 MVVM/MVC 意义上控制视图。 SwiftUI 自动为我们创建和更新屏幕上的视图,即 UILabels、UITableView 等。 SwiftUI View 结构本质上已经是视图模型,因此如果您要将其重新创建为一个对象,您不仅会不必要地使您的代码更加复杂,而且还会引入 SwiftUI 试图通过使用结构消除的对象引用错误。使用像 @State
和 @Binding
这样的 属性 包装器,SwiftUI 正在做一些神奇的事情来使结构表现得像一个对象,忽略它不是一个好主意。为了使您的 View 结构更易于测试,您可以将相关的变量提取到一个结构中并使用像这样的变异函数:
// View Model
struct ParkConfig {
var numberOfTrees = 0
mutating func plantTree() {
numberOfTrees += 1
}
}
struct ContentView {
@State var parkConfig = ParkConfig()
var body: some View {
ParkView(config: $parkConfig)
}
}
// View
struct ParkView: View {
@Binding var config: ParkConfig
var body: some View {
Button("Click Me") {
config.plantTree()
}
}
}
您可以看到 Apple 在 Data Essentials in SwiftUI WWDC 2020 at 4:18 中展示了这种模式,他说“EditorConfig 可以在其属性上保持不变量并独立测试。而且因为 EditorConfig 是一个值类型,任何更改属性 EditorConfig,就像它的进度一样,是对 EditorConfig 本身的更改。"