符合ObservableObject的SwiftUI 类 应该是Singleton?
SwiftUI Classes that conforms ObservableObject should be Singleton?
我被认为是 SwiftUI 的新手,我有以下 ViewModel。但我不确定 MyViewModel 应该是单例的。这种用法对吗?符合 ObservableObject 的最佳 practice/usage 是什么?
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)
}
}
为什么你认为视图模型应该是单例的?特别是,为什么 ObservableObject
符合 class 需要单例实例?这是个坏主意。
这不仅完全没有必要,而且还意味着如果没有共享状态,您不能在屏幕上拥有同一视图的多个实例。如果您想在屏幕上同时支持分屏和 运行 您的应用的 2 个场景,这在 iPad 上尤其糟糕。
除非万不得已,否则不要将任何东西设为单例。
在 SwiftUI View
s 上存储 @ObservedObject
s 时要记住的唯一重要的事情是它们永远不应该在视图内初始化。当 @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)
}
}
我就是这样实现我的场景的。我们能说这是正确的方法吗?谢谢...
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"
}
}
我被认为是 SwiftUI 的新手,我有以下 ViewModel。但我不确定 MyViewModel 应该是单例的。这种用法对吗?符合 ObservableObject 的最佳 practice/usage 是什么?
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)
}
}
为什么你认为视图模型应该是单例的?特别是,为什么 ObservableObject
符合 class 需要单例实例?这是个坏主意。
这不仅完全没有必要,而且还意味着如果没有共享状态,您不能在屏幕上拥有同一视图的多个实例。如果您想在屏幕上同时支持分屏和 运行 您的应用的 2 个场景,这在 iPad 上尤其糟糕。
除非万不得已,否则不要将任何东西设为单例。
在 SwiftUI View
s 上存储 @ObservedObject
s 时要记住的唯一重要的事情是它们永远不应该在视图内初始化。当 @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)
}
}
我就是这样实现我的场景的。我们能说这是正确的方法吗?谢谢...
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"
}
}