How to Validate Dynamically Added Textfields on a Button Click in Swiftui

How do I validate dynamically added textFields on a button click in SwiftUI?

You can be done this by making a model of text fields and use one isValid flag for each InputView for the track.

Here, is the possible demo solution.

struct TextFieldModel: Identifiable {
var id = UUID()
var input: String
var correctInput: Int
var isValidate: Bool = true
}

struct InputView: View {
@Binding var input: TextFieldModel
var body: some View {
TextField("?", text: $input.input)
.foregroundColor(input.isValidate ? Color.blue : Color.red)
}
}

struct ContentViewTextFields: View {
@State var arrTextFields: [TextFieldModel] = [
.init(input: "", correctInput: 5),
.init(input: "", correctInput: 10),
.init(input: "", correctInput: 1)
]

@State var isValidate: Bool = true

var body: some View {
VStack{
ForEach(arrTextFields.indices) { index in
InputView(input: $arrTextFields[index])
.background(Color.gray.opacity(0.2))
.padding()
}
Spacer()

Button("Validate") {
// Here validate all text
arrTextFields.indices.forEach({arrTextFields[$0].isValidate = (Int(arrTextFields[$0].input) == arrTextFields[$0].correctInput) })
}
}
}
}

Sample Image

Create empty Text Field in View with Button Click SwiftUI

You want a dynamic number of text fields, right? Whenever you want a dynamic number of views, you want ForEach. This generates a view for each element in an array.

struct TestView: View {
@State var ingredientNames = [""] /// array of ingredients

var body: some View {
VStack { /// vertical stack of ingredients
ForEach(ingredientNames.indices, id: \.self) { index in
TextField("Example Field", text: $ingredientNames[index]) /// use each element in the array
}

Button(action: {
/// Add another empty text field to the view
ingredientNames.append("")
}) {
Image(systemName: "plus.circle")
.foregroundColor(Color.black)
}
}
}
}

Result:

Press plus button to add text field

How can I update a TextField value and have its associated index in an array update as well?

I ended up solving the issue by converting @State var item: RecipeStepModel in my view model to @Binding var item: RecipeStepModel

struct CustomTextField : View {
@Binding var item : RecipeStepModel

var body : some View {
Text(String(item.stepNumber) + ".")
TextField("", text: $item.stepName)
}

Once this change was made, I had to alter the code in my ForEach to pass a binding to the CustomTextField view model. Additionally, I had to change ForEach(recipeArray, id: \.id) to ForEach(recipeArray.indices, id: \.self) :

 List {
ForEach(recipeArray.indices, id: \.self) { index in
HStack {
CustomTextField(item: $recipeArray[index]) //<--change made here
}
}.onDelete { (indexSet) in
recipeArray.remove(atOffsets: indexSet)
}

I am now able to both successfully delete items from the list, and update items in the array simply by changing the value in the appropriate TextField

Create an arbitrary number of state variables in SwiftUI

Using the strategy in the edit that you added, with the Dictionary, you could provide a custom Binding, like this:

func bindingForID(id: String) -> Binding<String> {
.init {
answers[id] ?? ""
} set: { newValue in
answers[id] = newValue
}
}

And you could use it like this:

TextField(surveyQuestion.placeholder, text: bindingForID(id: surveyQuestion.id))

In terms of adding this data to Firestore, you could trigger Firestore updates in the set closure from the custom binding. However, I'd probably recommend moving this logic to a @Published property on a view model (ObservableObject) where you could use Combine to do things like Debouncing before you send the data to Firestore (probably a little beyond the scope of this question).

Swift - validating UITextField

Alternatively, you can use this, which is called every time a key is pressed:

name1.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
name2.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
name3.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
name4.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)


func textFieldDidChange(textField: UITextField) {
if name1.text?.isEmpty || name2.text?.isEmpty || name3.text?.isEmpty || name4.text?.isEmpty {
//Disable button
} else {
//Enable button
}
}

Dynamically add cells to SwiftUI LazyGrid

You should use dynamic variant of ForEach in this case, like

var body: some View {
LazyVGrid(columns: gridLayout) {
ForEach(counter.indices, id: \.self) { item in // << here !!

textFieldDidBeginEditing and textFieldDidEndEditing in SwiftUI

TextField has onEditingChanged and onCommit callbacks.

For example:

@State var text = ""
@State var text2 = "default"
var body: some View {
VStack {
TextField($text, placeholder: nil, onEditingChanged: { (changed) in
self.text2 = "Editing Changed"
}) {
self.text2 = "Editing Commited"
}
Text(text2)
}
}

The code in onEditingChanged is only called when the user selects the textField, and onCommit is only called when return, done, etc. is tapped.

Edit: When the user changes from one TextField to another, the previously selected TextField's onEditingChanged is called once, with changed (the parameter) equaling false, and the just-selected TextField's onEditingChanged is also called, but with the parameter equaling true. The onCommit callback is not called for the previously selected TextField.

Edit 2:
Adding an example for if you want to call a function committed() when a user taps return or changes TextField, and changed() when the user taps the TextField:

@State var text = ""
var body: some View {
VStack {
TextField($text, placeholder: nil, onEditingChanged: { (changed) in
if changed {
self.changed()
} else {
self.committed()
}
}) {
self.committed()
}
}
}

How to check if a text field is empty or not in swift

Simply comparing the textfield object to the empty string "" is not the right way to go about this. You have to compare the textfield's text property, as it is a compatible type and holds the information you are looking for.

@IBAction func Button(sender: AnyObject) {
if textField1.text == "" || textField2.text == "" {
// either textfield 1 or 2's text is empty
}
}

Swift 2.0:

Guard:

guard let text = descriptionLabel.text where !text.isEmpty else {
return
}
text.characters.count //do something if it's not empty

if:

if let text = descriptionLabel.text where !text.isEmpty
{
//do something if it's not empty
text.characters.count
}

Swift 3.0:

Guard:

guard let text = descriptionLabel.text, !text.isEmpty else {
return
}
text.characters.count //do something if it's not empty

if:

if let text = descriptionLabel.text, !text.isEmpty
{
//do something if it's not empty
text.characters.count
}

How do I check when a UITextField changes?

SWIFT

Swift 4.2

textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

and

@objc func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 3 & swift 4.1

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

and

func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 2.2

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)

and

func textFieldDidChange(textField: UITextField) {
//your code
}

OBJECTIVE-C

[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

and textFieldDidChange method is

-(void)textFieldDidChange :(UITextField *) textField{
//your code
}


Related Topics



Leave a reply



Submit