1
votes

uialert controller

This is my ui alert controller. I need to have the submit button disabled if any of the text fields are empty. Currently I have a method to validate a single text field, but I cant seem to figure out how to apply it to all of them. This is my alert controller and text fields:

  let ac = UIAlertController(title: "Enter Custom Specifications", message: nil, preferredStyle: .alert)
    ac.addTextField { (textField : UITextField!) -> Void in
        textField.placeholder = "Material Name"
        textField.addTarget(self, action: #selector(self.textFieldDidChange(textField:)), for: .editingChanged)
    }

    ac.addTextField { (textField : UITextField!) -> Void in
        textField.placeholder = ""
        textField.keyboardType = UIKeyboardType.decimalPad
    }

    ac.addTextField { (textField : UITextField!) -> Void in
        textField.placeholder = ""
        textField.keyboardType = UIKeyboardType.decimalPad
    }

    ac.addTextField { (textField : UITextField!) -> Void in
        textField.placeholder = ""
        textField.keyboardType = UIKeyboardType.decimalPad
    }

Using this function called in the first textfield above I can get proper empty string validation per textfield, meaning submit is disabled while that first textfield is empty but then submit becomes enabled when I start typing. If I put the function call in each textfield declaration, the same behavior is present for each one.

 @objc func textFieldDidChange(textField: UITextField){
    if textField.text == "" {

        submitAction.isEnabled = false
    }
    else
    {
        submitAction.isEnabled = true

    }

}

I need a way to check all textfields at the same time and have submit disabled if any of them are empty.

EDIT:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if textField.text == "" {
        //You can perfrom your activity here when textfield got blank or empty
        submitAction.isEnabled = false
    }
    else if textField.text != ""
    {
        submitAction.isEnabled = true
        //You can perfrom your activity here when textfield got not blank or not empty
    }
    //add your code here...
    return true
}

EDIT2:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if let text = (textField.text as? NSString)?.replacingCharacters(in: range, with: string), !text.isEmpty {
        if let text = textField.text?.replacingCharacters(in: range, with: string), !text.isEmpty {
            submitAction.isEnabled = true
        } else {
            submitAction.isEnabled = false
        }
        return true
    }}

EDIT 3: Instead of all the textfields just being called "textfield" I can name them textfield"x" when I create them:

ac.addTextField { (textField2 : UITextField!) -> Void in
        textField2.placeholder = "Column Width (in)"
        textField2.keyboardType = UIKeyboardType.decimalPad
        textField2.delegate = self
    }

How can I get shouldChangeCharactersIn to accept them all at the same time?

EDIT (solution):

Ok I figured it out. PGDev was correct on the logic part of it but not the delegate function used to actually check. I'm going to mark his answer correct since I wouldn't have gotten to this point without it.

Initially we were using textFieldDidEndEditing, and I thought it just simply wasn't working because it wouldn't reenable the submit button once all four text fields were filled. What I didn't realize is that I had to tap an additional textfield after I was done for textFieldDidEndEditing to be called. Makes sense since it has "EndEditing" in the name.

I took PGDevs logic and put it in shouldChangeCharactersIn which worked but not perfectly. I'm not sure if there's an internal issue with it but when you try to fill the last text box sometimes backspace (or something) is interpreted as a character and it throws the enable / !enable statement out of wack.

textFieldDidChange using PGDevs logic was the answer.

set class wide variables for how many text boxes you're going to add to your alert controller:

var textField1: UITextField?
var textField2: UITextField?
var textField3: UITextField?
var textField4: UITextField?

Put this function at the bottom of your class function:

   @objc func textFieldDidChange(_ textField: UITextField) {

    let text1 = self.textField1?.text ?? ""
    let text2 = self.textField2?.text ?? ""
    let text3 = self.textField3?.text ?? ""
    let text4 = self.textField4?.text ?? ""

    if !text1.isEmpty && !text2.isEmpty && !text3.isEmpty && !text4.isEmpty {
        submitAction.isEnabled = true
    } else {
        submitAction.isEnabled = false
    }

}

When you generate an actual text field in the alert controller, call the above function:

 let ac = UIAlertController(title: "Enter Custom Specifications", message: nil, preferredStyle: .alert)
    ac.popoverPresentationController?.sourceView = self.view
    ac.addTextField { (textField : UITextField!) -> Void in
        textField.delegate = self
        textField.placeholder = "Material Name"
        self.textField1 = textField
        textField.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: .editingChanged)
    }

This works perfectly for me. unless all 4 rows have at least one character in them, submit stays disabled. This could be used for as many text fields as you want.

1

1 Answers

2
votes

Try setting the delegate of each textField as the viewCobtroller where you're presenting the alert, i.e.

textField.delegate = self

Conform the ViewController to UITextFieldDelegate protocol,

class ViewController: UIViewController, UITextFieldDelegate {

Implemennt the relevant UITextFieldDelegate methods, Example:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    if let text = (textField.text as? NSString)?.replacingCharacters(in: range, with: string), !text.isEmpty {
        submitAction.isEnabled = true
    } else {
        submitAction.isEnabled = false
    }
    return true
}

Edit:

You need to keep the reference to all the textFields so you can check the text in all of them to enable/disable the submitAction, i.e.

var textField1: UITextField?
var textField2: UITextField?
var textField3: UITextField?
var textField4: UITextField?

Set each of the textFields while adding to the UIAlertController, i.e.

let ac = UIAlertController(title: "Enter Custom Specifications", message: nil, preferredStyle: .alert)
ac.addTextField { (textField : UITextField!) -> Void in
    textField.delegate = self
    textField.placeholder = "Material Name"
    self.textField1 = textField
}
ac.addTextField { (textField : UITextField!) -> Void in
    textField.delegate = self
    textField.keyboardType = UIKeyboardType.decimalPad
    self.textField2 = textField
}
ac.addTextField { (textField : UITextField!) -> Void in
    textField.delegate = self
    textField.keyboardType = UIKeyboardType.decimalPad
    self.textField3 = textField
}
ac.addTextField { (textField : UITextField!) -> Void in
    textField.delegate = self
    textField.keyboardType = UIKeyboardType.decimalPad
    self.textField4 = textField
}

Call validate() everytime there is a change in any of the textFields, i.e.

func textFieldDidEndEditing(_ textField: UITextField) {
    self.validate()
}


func validate() {
    let text1 = self.textField1?.text ?? ""
    let text2 = self.textField2?.text ?? ""
    let text3 = self.textField3?.text ?? ""
    let text4 = self.textField4?.text ?? ""

    if !text1.isEmpty && !text2.isEmpty && !text3.isEmpty && !text4.isEmpty {
        submitAction.isEnabled = true
    } else {
        submitAction.isEnabled = false
    }
}