1

我在 SwiftUI 中被认为是新手,并且我有以下 ViewModel。但我不确定 MyViewModel 应该是单例的。这种用法对吗?什么是符合 ObservableObject 的最佳实践/用法?

class MyViewModel: ObservableObject {
    static let shared: MyViewModel = MyViewModel()
    
    @Published var result: String = ""
    
    private init() { }
    
    // some functions
}

struct ContentView: View {
    @ObservedObject private var vm = MyViewModel.shared
    
    var body: some View {
        Text(vm.result)
    }
}
4

2 回答 2

3

为什么你认为视图模型应该是单例?尤其是,为什么一个ObservableObject符合要求的类需要一个单例实例?这是个坏主意。

这不仅是绝对不必要的,这也意味着你不能在屏幕上拥有相同视图的多个实例,而它们没有共享状态。如果您想支持分屏并在屏幕上同时运行应用程序的 2 个场景,这在 iPad 上尤其糟糕。

不要做任何单例,除非你绝对必须这样做。

@ObservedObject在 SwiftUI上存储 s 时要记住的唯一重要的事情View是,它们永远不应该在视图中初始化。当一个@ObservedObject改变(或它的一个@Published属性改变)时,View它的存储将被重新加载。这意味着如果您在 中创建对象View,则每当对象更新时,视图本身都会创建所述对象的新实例。

所以这是一个坏主意,并且行不通:

struct ContentView: View {
    // Never do this
    @ObservedObject private var vm = MyViewModel()
    
    var body: some View {
        Text(vm.result)
    }
}

相反,您需要将视图模型注入您的View(通过在父视图或协调器等中创建它,无论您ContentView从何处创建)。

struct ParentView: View {
    @State private var childVM = MyViewModel()

    var body: some View {
        ContentView(vm: childVM)
    }
}


struct ContentView: View {
    @ObservedObject private var vm: MyViewModel
 
    // Proper way of injecting the view model
    init(vm: MyViewModel) {
        self.vm = vm
    }
   
    var body: some View {
        Text(vm.result)
    }
}
于 2020-09-18T12:31:46.930 回答
0

我通过这种方式实现了我的场景。我们可以说这是正确的方法吗?谢谢...

struct RootTabView: View {
    @State var tabSelection = 0
    @State private var listVM = ListViewModel()
    
    var body: some View {
        TabView(selection: $tabSelection) {
            ListView(vm: listVM).tabItem({
                Text("Tab 1")
            }).tag(0)
            
            //Some other tabs
        }
    }
}

struct ListView: View {
    @ObservedObject var vm: ListViewModel
    
    var body: some View {
        NavigationView {
            List(vm.toDoList, id: \.self) { toDo in
                NavigationLink(destination: DetailView(vm: vm)) {
                    Text(toDo)
                }
            }
        }
        .onAppear {
            vm.getList()
        }
    }
}

struct DetailView: View {
    @ObservedObject var vm: ListViewModel
    
    var body: some View {
        Text(vm.toDoItem)
            .onAppear {
                vm.getDetail()
            }
    }
}

class ListViewModel: ObservableObject {
    @Published var toDoList: [String] = []
    @Published var toDoItem: String = ""
    
    func getList() {
        toDoList = ["a", "b", "c"]
    }
    
    func getDetail() {
        // do some stuffs
        toDoItem = "A"
    }
}
于 2020-09-18T14:18:01.990 回答