SwiftUI - Right approach to disabling a Button until 2 TextFields have text in them

Greetings,


I'm currently evaluating SwiftUI, and looking to replicate the common functionality

of disabling a button until text/string are entered in 2 fields.


Common Scenario:


A login screen with UserName & Password enables the Login Button once values

are contained. This is a simple affair on Swift using typical UIKit.


This is accomplished quite simply with property observers and leveraging didSet, and a function to check.

I can post this code, however it's not important here.


With SwiftUI, Is there a suggested approach to Bind two fields to a button?


I've experimented with a few things like onEditingChanged, and onCommit.

A simplified version here:


import SwiftUI

struct ContentView: View {
      @State private var username = ""
      @State private var password = ""

 var body: some View {
        NavigationView{
            VStack {
                
                TextField("enter user name", text: $username, onEditingChanged: { (changed) in
                    print("editing \(self.username)")
                }) {
                    print("committed \(self.username)")
                }
                .padding(10)

                SecureField("enter a password", text: $password, onCommit: { () in
                    print("committed password \(self.password)")
                })
                .padding(10)
                
                Button(action: {
                    // show next screen here/nav
                }) {
                    Text("Login")
                        .padding(10)
                        .font(.title)
                        
                        Spacer()
                    }.padding(10)
                    .disabled(true) // ideally pass this a condition where both username and password have values
                 }
            }
        }

}


I've also created a custom button as such, but no luck just yet


struct RoundedButton : View {
        @Binding var usernameEntered: Bool
        @Binding var passwordEntered: Bool
        
        var body: some View {

            Button(action: {}) {
                HStack{

                    Spacer()
                    Text("Login")
                        .padding(10)
                        .font(.title)
                    Spacer()
                }
              
            }
            .disabled(passwordEntered && usernameEntered)
            .background(Color.blue)
            .foregroundColor(.white)
            .cornerRadius(8)
        }
    }


Thoughts anyone?

Answered by Jim Dovey in 390976022

So long as the condition passed to

.disabled()
makes use of
@State
variables or similar, it will be automatically re-evaluated when those variables change (or rather, the view's
body
will be invoked again, thus running the check). Ultimately all you need to do is use the content of the
username
and
password
properties to create your condition.


Here's an example that disables the button if either the username or password is empty:


struct ContentView: View {
    @State private var username = ""
    @State private var password = ""
    
    var body: some View {
        VStack {
            TextField("enter user name", text: $username)
                .padding(10)
            
            SecureField("enter a password", text: $password)
                .padding(10)
            
            Button(action: {
                // show next screen here/nav
            }) {
                Text("Login")
                    .padding(10)
                    .font(.title)
                
                Spacer()
            }
            .padding(10)
            .disabled(username.isEmpty || password.isEmpty)
        }
    }
}
Accepted Answer

So long as the condition passed to

.disabled()
makes use of
@State
variables or similar, it will be automatically re-evaluated when those variables change (or rather, the view's
body
will be invoked again, thus running the check). Ultimately all you need to do is use the content of the
username
and
password
properties to create your condition.


Here's an example that disables the button if either the username or password is empty:


struct ContentView: View {
    @State private var username = ""
    @State private var password = ""
    
    var body: some View {
        VStack {
            TextField("enter user name", text: $username)
                .padding(10)
            
            SecureField("enter a password", text: $password)
                .padding(10)
            
            Button(action: {
                // show next screen here/nav
            }) {
                Text("Login")
                    .padding(10)
                    .font(.title)
                
                Spacer()
            }
            .padding(10)
            .disabled(username.isEmpty || password.isEmpty)
        }
    }
}

Worked like a charm thank you Jim!

Marvelous!


Can you mark the answer as the correct solution (under the Actions pop-up I think)? That way it'll be highlighted on the menu to help people with similar queries find it.

Absolutely, my pleasure!

SwiftUI - Right approach to disabling a Button until 2 TextFields have text in them
 
 
Q