Reputation: 4233
Consider the following code:
import SwiftUI
class ViewModel: ObservableObject {
}
struct TestView: View {
@ObservedObject var vm = ViewModel()
var body: some View {
// self.sample
GeometryReader { _ in
self.sample
}
}
var sample: some View {
Text("Hello, World!")
}
}
struct Tabs : View {
@State var selection: Int = 0
var body: some View {
TabView(selection: $selection) {
TestView().tabItem {
Text("First Tab")
}
.tag(0)
Text(String(selection))
.tabItem {
Text("Second Tab")
}
.tag(1)
}
}
}
struct TestView_Previews: PreviewProvider {
static var previews: some View {
TestView()
}
}
There are two tabs and selection is referenced in body therefore body will be called when selection is changed. TestView is using GeometryReader. When I switch from "First Tab" to "Second Tab" ViewModel is created again and never dereferenced. This is unexpected. If I switch 100 times I will have 100 ViewModels referenced from SwiftUI internals.
Though if i remove GeometryReader it works as expected.
Did someone experience it? Are there any workarounds? I simply want this ViewModel lifetime to be bound to TestView lifetime.
UPDATE:
XCode 11.3.1 iOS 13.3
Upvotes: 1
Views: 1183
Reputation: 257493
Ok, let's make the following changes in ViewModel
class ViewModel: ObservableObject {
init() {
print(">> inited") // you can put breakpoint here in Debug Preview
}
}
so now it seen that because View is value type
struct TestView: View {
@ObservedObject var vm = ViewModel() // << new instance on each creation
...
and it is originated from
var body: some View {
TabView(selection: $selection) {
TestView().tabItem { // << created on each tab switch
...
so, the solution would be to ViewModel
creation out of TestView
and inject outer instance either via .environmentObject
or via constructor arguments.
Btw, it does not depend on GeometryReader
. Tested with Xcode 11.2.1 / iOS 13.2
Upvotes: 4