Reputation: 9773
I am having trouble figuring out how to dismiss a view that was presented fullscreen in SwiftUI.
On the sign in page, there is a tap to reset password option. When the user taps there, another view appears. The new view has a different ViewModel than that of the SignInView.
After an asynchronous operation to reset the password, I would like to dismiss the reset password view and go back to the SignInView. An abbreviated version looks like this:
// SignInView
@State var isActive = false
var body: some View {
Text("Forgot Password? Tap to reset")
.onTapGesture {
isActive = true
}
.fullScreenCover(isPresented: $isActive, content: {
ResetPasswordView(viewModel: ResetPasswordViewModel(), isActive: $isActive)
})
}
// ResetPasswordView
@ObservedObject var viewModel: ResetPasswordViewModel
@Binding var isActive: Bool
init(viewModel: ResetPasswordViewModel, isActive: Binding<Bool>) {
self.viewModel = viewModel
self._isActive = isActive
}
var body: some View {
Button(action: {
viewModel.resetPassword()
}, label: {
Text("Done")
})
}
Because the asynchronous password reset operation takes place in the ResetPasswordView's ViewModel, I want to be able to set the isActive to false once it's done and dismiss the ResetPasswordView. But I don't know how to change the isActive from the ViewModel as it was passed directly to the view.
I'm very new to MVVM and not sure I am thinking about this the correct way.
Upvotes: 1
Views: 2762
Reputation: 637
Don't bind view's property to viewmodel, viewmodel should manage the view all property state to publish to the view.
// SignInView
@ObservedObject var viewModel = CurrentPageViewModel()
var body: some View {
Text("Forgot Password? Tap to reset")
.onTapGesture {
viewModel.push()
}
.fullScreenCover(isPresented: $viewModel.isActive, content: {
ResetPasswordView()
})
}
class CurrentPageViewModel: ObservableObject {
@Published var isActive: Bool = false
func push() {
isActive = true
}
}
Upvotes: 0
Reputation: 20274
You have binded isActive
properly. Now you just need to set it to false
.
All you need is the following at the appropriate place:
isActive = false
One way is to let the ViewModel
handle isActive
directly.
class ResetPasswordViewModel: ObservableObject {
@Binding var isActive: Bool
//...other viewModel variables
init(/*...your other params*/ isActive: Binding<Bool>) {
self._isActive = isActive
}
func resetPassword() {
//...your logic
isActive = false
}
}
struct ContentView: View {
@State var isActive = false
var body: some View {
Text("Forgot Password? Tap to reset")
.onTapGesture { isActive = true }
.fullScreenCover(isPresented: $isActive,
content: {
ResetPasswordView(viewModel: ResetPasswordViewModel(isActive: $isActive))
})
}
}
struct ResetPasswordView: View {
@ObservedObject var viewModel: ResetPasswordViewModel
var body: some View {
Button(action: {
viewModel.resetPassword()
}, label: {
Text("Done")
})
}
}
Upvotes: 0