Now, it would seem a NavigationView is the most suitable type to move the user between these views.
For example, when the user enters a recognized email and password, the two factor view pushes in. If the user decides to use a different credential, they can tap or gesture back. Or they do not enter the code from the second factor fast enough, they should be sent back to the email/password view.
Right now, I can't seem to do this without having a button the user must use themselves. Scouring the internet doesn't seem to give any insights.
Any ideas?
Thanks for your time.
struct ContentView: View {
var body: some View {
LoginView()
}
}
enum Stage: Hashable {
case form
case twoFactor
}
struct LoginView: View {
@State var stage: Stage? = .form
var body: some View {
NavigationView {
LoginFormView(stage: $stage)
NavigationLink(destination: TwoFactorAuthenticationView(), tag: Stage.twoFactor, selection: $stage) {
EmptyView()
}
}
}
}
struct LoginFormView: View {
@ObservedObject var loginForm = LoginForm()
@Binding var stage: Stage?
var body: some View {
VStack {
TextField("Email", text: $loginForm.email)
.textContentType(.emailAddress)
SecureField("Password", text: $loginForm.password, onCommit: {
self.logIn()
}).textContentType(.password)
Button(action: {
self.logIn()
}, label: {
Text("Log In")
})
}.navigationBarTitle("Log In")
}
func logIn() {
loginForm.logIn { result in
switch result {
case .success(let session):
print(session)
self.stage = .twoFactor
case .failure(let error):
print(error)
}
}
}
}
struct TwoFactorAuthenticationView: View {
var body: some View {
Text("Hi").navigationBarTitle("2FA")
}
}
Your suggestion to remove the EmptyView() and replace it with Text is not ideal because it places the text content under the "Waiting..." text. The idea was to avoid having any visible reference to the possible next screen.
However, it seems you can still use an empty view so long as the content of the NavigationView is contained in a VStack. If you remove the VStack from the following code, it doesn't work, which is extremely counter-intuitive:
struct ColourView: View {
enum PushedColor: Hashable {
case red
case green
}
@State var pushed: PushedColor?
var body: some View {
NavigationView {
VStack {
Text("Waiting...")
NavigationLink(destination: Text("Red"), tag: .red, selection: $pushed) {
EmptyView()
}
NavigationLink(destination: Text("Green"), tag: .green, selection: $pushed) {
EmptyView()
}
}
}
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
self.pushed = .green
}
}
}
}
So, the above code works. Remove the VStack and it doesn't.