观察Swift结构的变化



我希望我的MVVM架构有一个类作为模型和结构作为视图模型或视图控制器。这是因为我想重新初始化一堆属性改变模型和使用类作为视图模型只允许一个init。但是,它还没有找到一种方法来让视图观察视图模型中新的结构或对结构的更改。

我的问题的例子:

class Model: ObservableObject {
var nums: [Int]

init() {
self.nums = Array(1..<100)
}

func getNum() -> Int {
return nums.count
}

func add() {
nums.append(nums.count + 1)
self.objectWillChange.send()
}
}
struct ViewModel {
var model: Model
var num: Int

init(model: Model) {
self.model = model
self.num = model.getNum()
}

func trigger() {
model.add()
print("Triggered")

}
}

struct ContentView: View {
var viewModel: ViewModel
var body: some View {
Button(action: {viewModel.trigger() }) {
Text("Press")
}
Text("Number of Elements")
Text("(viewModel.num)")
}

}
var model = Model()
var viewModel = ViewModel(model: model)
var view = ContentView(viewModel: viewModel)

@main
struct app: App {
var body: some Scene {
WindowGroup {
view
}
}
}

如果您的主要目标是根据对模型的更改在视图模型上计算属性,那么我将如何解决这个问题:

import Combine
struct Model {
var nums: [Int]

init() {
self.nums = Array(1..<100)
}

func getNum() -> Int {
return nums.count
}
//note there aren't mutating methods here
}
class ViewModel : ObservableObject {
@Published var model: Model
@Published var num: Int = -1

private var cancellable : AnyCancellable?

init(model: Model) {
self.model = model
cancellable = $model.sink(receiveValue: { newValue in
self.num = newValue.getNum() //calculated based on the new value
})
}

func trigger() {
self.add()
print("Triggered")
}

//mutating method here
func add() {
model.nums.append(model.nums.count + 1)
}
}

struct ContentView: View {
@StateObject var viewModel: ViewModel = ViewModel(model: Model())
var body: some View {
Button(action: {viewModel.trigger() }) {
Text("Press")
}
Text("Number of Elements")
Text("(viewModel.num)")
}

}

更新,如果你想在模型中改变函数:

struct Model {
var nums: [Int]

init() {
self.nums = Array(1..<100)
}

func getNum() -> Int {
return nums.count
}
mutating func add() {
nums.append(nums.count + 1)
}
}
class ViewModel : ObservableObject {
@Published var model: Model
@Published var num: Int = -1

private var cancellable : AnyCancellable?

init(model: Model) {
self.model = model
cancellable = $model.sink(receiveValue: { newValue in
self.num = newValue.getNum() //calculated based on the new value
})
}

func trigger() {
model.add()
print("Triggered")
}
}

我想我看到的第一件事是你把可观察对象放在你的模型中,在mvvm中,它试图把那个属性放在视图模型中,因为它需要响应,一个模型只接收一些属性,你可以改变属性到它的视图模型,并能够使它响应。

如果你使用一个类作为模型,它仍然可以工作,这是你喜欢的。

class Model{
var nums: [Int]
init() {
self.nums = Array(1..<100)
}
func getNum() -> Int {
return nums.count
}
func add() {
nums.append(nums.count + 1)
self.objectWillChange.send()
}
}
struct ViewModel:ObservableObject  {
var model: Model
var num: Int
init(model: Model) {
self.model = model
self.num = model.getNum()
}
func trigger() {
model.add()
print("Triggered")

}
}

struct ContentView: View {
@observedObject var viewModel: ViewModel 
var body: some View {
Button(action: {viewModel.trigger() }) {
Text("Press")
}
Text("Number of Elements")
Text("(viewModel.num)")
}
}
var model = Model()
var viewModel = ViewModel(model: model)
var view = ContentView(viewModel: viewModel)

@main
struct app: App {
var body: some Scene {
WindowGroup {
view
}
}
}

这是一个非常简单的方法,可以达到我想要的效果。不要使用结构作为视图模型/控制器,而是使用类——现在模型和视图模型都是类。当然,这样做的问题在于,类是一种引用类型,不会像结构体那样重新初始化,而结构体是随着更改而重新创建的。所以我不依赖init方法来设置我的属性——我在知道ViewModel类的trigger方法发生了变化后更新它们。下面是一个例子:

class Model {
var nums: [Int]

init() {
self.nums = Array(1..<100)
}

func getNum() -> Int {
return nums.count
}

func add() {
nums.append(nums.count + 1)
}
}

func updateProperty(model: Model) -> Int {
return model.getNum()
}

class ViewModel: ObservableObject {
var model: Model
var num: Int

init(model: Model) {
self.model = model
self.num = updateProperty(model: model)
}

func trigger() {
model.add()
self.num = updateProperty(model: self.model)
self.objectWillChange.send()
}
}

struct ContentView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
Button(action: {viewModel.trigger() }) {
Text("Press")
}
Text("Number of Elements")
Text("(viewModel.num)")
}
}
var model = Model()
var viewModel = ViewModel(model: model)
var view = ContentView(viewModel: viewModel)

显然,在大多数实际情况下,updateProperty函数将比仅仅一个Int更复杂

最新更新