Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI: Enable save button only when the inputs are not empty

I've got a view with two text fields and a save button. How can I possibly change my button's state depending on the contents of the text fields (I'd like to enable the button only in case all text fields are not empty)? Here's my current code:

// The variables associated with text fields
@State var name: String = ""
@State var type: String = ""

// I'd like to associate this variable with
// my button's disabled / enabled state,
// but the function responsible for it doesn't accept bindings
@State var canSave: Bool = false

var body: some View {
    Form {
        TextField("Name", text: $name)
        TextField("Type", text: $type)

        Button(action: {
            // ...
        }, label: {
            Text("Save")
        })
            .disabled(!canSave) // no bindings allowed here; what to use indead?
    }
}

I have an idea that I should use combineLatest from the newest Combine framework. But whatever I try to google leads me to RxSwift-related topics and not actual Combine implementation.

like image 794
Andrew Avatar asked Nov 19 '19 20:11

Andrew


2 Answers

You seem to have a misconception about how SwiftUI works. It doesn't depend for downhill data flow on bindings. It depends purely on State variables. Uphill flow depends on bindings, but you've got those just where you need them (except that your code is wrong: you've bound both text fields to the same binding).

So in a simple case like this, you don't need a binding or Combine. You have State variables and that's all you need:

struct ContentView: View {
    @State var name: String = ""
    @State var type: String = ""
    var body: some View {
        Form {
            TextField("Name", text: $name)
            TextField("Type", text: $type)
            Button(action: {
                // ...
            }, label: {
                Text("Save")
            }).disabled(name.isEmpty || type.isEmpty)
        }
    }
}

Now, if you had many text fields to validate instead of just one or two, then sure, you could use publish and subscribe to combine them into a single Bool. But let's get the basic principles right first.

like image 54
matt Avatar answered Nov 15 '22 00:11

matt


You can add some codes at the end of the View. It's just for simple logic, otherwise, you may need to create some models.

 var body: some View {
Form {
    TextField("Name", text: $name)
    TextField("Type", text: $type)

    Button(action: {
        // ...
    }, label: {
        Text("Save")
    })
        .disabled(!canSave) // no bindings allowed here; what to use indead?
}.onReceive(Publishers.CombineLatest(Just( name.isEmpty), Just(type.isEmpty))){
    self.canSave = !($0.0 || $0.1)
}
like image 38
E.Coms Avatar answered Nov 15 '22 01:11

E.Coms