简体   繁体   中英

SwiftUI: ObservedObject/ObservableObject weird behavior

I recently encountered the following problem in SwiftUI with ObservedObject / ObservableObject :

If the ObservedObject / ObservableObject is publishing in a View, the body property is recalculated - as expected.

But if there is a sub View in the body property which also has an ObservedObject , the View, strangely enough, not only recalculates the body property of the sub View, but the entire object .

The state of the ObservedObject is of course lost from the sub View.

Of course, you can prevent this by adding the ObservableObject to the sub View through .environmentObject() , but I don't think that's the best solution, especially with more complex view hierarchies.

Here an example Code:

struct ContentView: View {
    
    @ObservedObject var contentViewModel: ContentViewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Button {
                self.contentViewModel.counter += 1
            } label: {
                Text(String(self.contentViewModel.counter))
            }
            
            SubView()
        }
    }
}
class ContentViewModel: ObservableObject {
    @Published var counter: Int = 0
}

And the sub View:

struct SubView: View {
    
    @ObservedObject var subViewModel: SubViewModel = SubViewModel()
    
    
    var body: some View {
        Button {
            self.subViewModel.counter += 1
        } label: {
            Text(String(self.subViewModel.counter))
        }
    }
}
class SubViewModel: ObservableObject {
    @Published var counter: Int = 0
}

And here how the sample Code looks/works:

预习

The last weird thing I realised, this is only the case if you use Observed Object. If I would replace in the sub View @ObservedObject var subViewModel: SubViewModel = SubViewModel() with @State var counter: Int = 0 it is working fine again and the state is preserved.

Maybe im missing out on something, but im really confused. Im very grateful for any answers and solutions. If you have further questions fell free to leave a comment, I will answer it within 24h (as long as the question is open).

Declare your ViewModel with @StateObject . @StateObject is not recreated for every view re-render more

struct SubView: View {
    @StateObject var subViewModel: SubViewModel = SubViewModel() //<--- Here
    
    var body: some View {
        Button {
            self.subViewModel.counter += 1
        } label: {
            Text(String(self.subViewModel.counter))
        }
    }
}

I found a perfect solution:

Either you replace the @ObservedObject with @StateObject (thx @Raja Kishan) in iOS 14 (SwiftUI v2.0) or you can create a Wrapper for the View and simulate the behaviour of @StateObject in iOS 13 (SwiftUI v1.0):

struct SubViewWrapper: View {
    @State var subViewModel: SubViewModel = SubViewModel()

    var body: some View {
        SubView(subViewModel: self.subViewModel)
    }
}

and then use SubViewWrapper() in ContentView instead of SubView()

struct ContentView: View {
    
    @ObservedObject var contentViewModel: ContentViewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Button {
                self.contentViewModel.counter += 1
            } label: {
                Text(String(self.contentViewModel.counter))
            }
            
            SubViewWrapper()
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM