7

I have these outlets in my app:

@IBOutlet var name1: UITextField!

@IBOutlet var name2: UITextField!

@IBOutlet var name3: UITextField!

@IBOutlet var name4: UITextField!

@IBOutlet var newButton: UIButton!

What I tried to do is the following:

Every time the user types something in one of these four UITextFields or deletes something, I want to check if any UITextField is empty

If any UITextField is empty, the button should be disabled.

If all UITextFields are set (not empty), the button should be enabled.

My code:

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

    setButton()

    return true
}

func setButton() {

    let inputValid = checkInput()

    if inputValid {

        newButton.enabled = true

    } else {

        newButton.enabled = false

    }

}

func checkInput() -> Bool {

    let name1Value = name1.text
    let name2Value = name2.text
    let name3Value = name3.text
    let name4Value = name4.text

    if !name1Value.isEmpty && !name2Value.isEmpty && !name3Value.isEmpty && !name4Value.isEmpty {

        return true

    }

    return false

}

Ok, it works 50% for now.

When I type one character in each UITextField, the button is still disabled.

When I add a second one to any UITextField, the button gets enabled etc...

Can anyone help me with this?

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Creative crypter
  • 1,348
  • 6
  • 30
  • 67

6 Answers6

16

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
    }
}
Rakesha Shastri
  • 11,053
  • 3
  • 37
  • 50
brimstone
  • 3,370
  • 3
  • 28
  • 49
9

Swift 4 - Here is how I have solved it trying to avoid long conditionals - This will also allow you to do realtime validation on each individual textfield, unlike the accepted answer, you can update your UI according to what the user is typing.

let textfields : [UITextField] = [name1, name2, name3, name4]
for textfield in textfields {
  textfield.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}


@objc func textFieldDidChange(_ textField: UITextField) {
  //set Button to false whenever they begin editing
  yourButton.isEnabled = false
  guard let first = textFields[0].text, first != "" else {
        print("textField 1 is empty")
        return
    }
    guard let second = textFields[1].text, second != "" else {
        print("textField 2 is empty")
        return
    }
    guard let third = textFields[2].text, third != "" else {
        print("textField 3 is empty")
        return
    }
    guard let forth = textFields[3].text, forth != "" else {
        print("textField 4 is empty")
        return
    }
    // set button to true whenever all textfield criteria is met.
    yourButton.isEnabled = true

}
Vitalii
  • 4,267
  • 1
  • 40
  • 45
A. Welch
  • 356
  • 1
  • 3
  • 8
  • 1
    This is the straightforward way to do validation. With shouldChangeCharactersInRange there is a risk to get stuck with a big number of variations: user pastes a text, user enters in the mid of text or deletes in the mid of text - they all have to be taken into account. But with textFieldDidChange we just have the end result and can validate it as is. – Vitalii Mar 06 '19 at 17:16
  • I managed to work with that by using `let nsString = textField.text as NSString?` and `let newString = nsString?.replacingCharacters(in: shouldChangeCharactersIn, with: replacementString)` which gives you the newString. You can then validate that and return `true` or `false` as required. (Source: https://stackoverflow.com/questions/25138339/nsrange-to-rangestring-index) –  Mar 18 '21 at 16:36
2

Swift 4 & 5

Firstly, you create a function like setupTextFields() and you call it in viewDidLoad() (something like this):

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    setupTextFields() //You call the function here.
}

func setupTextFields() {
    name1.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
    name2.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
    name3.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
    name4.addTarget(self, action: #selector(self.textFieldDidChange(_:)), for: UIControl.Event.editingChanged)
}

Then you create another function which each time you press a key, that function will be called. I'll show you two options, then you choose which fits better for you. Usually, Option 1 would be considered a better implementation for Swift.

Option 1

@objc func textFieldDidChange(_ textField: UITextField) {
    guard !name1.text!.isEmpty else {
        button.isEnabled = false
        return
    }
    guard !name2.text!.isEmpty else {
        button.isEnabled = false
        return
    }
    guard !name3.text!.isEmpty else {
        button.isEnabled = false
        return
    }
    guard !name4.text!.isEmpty else {
        button.isEnabled = false
        return
    }
    button.isEnabled = true
}

Option 2

@objc func textFieldDidChange(_ textField: UITextField) {
    if name1.text!.isEmpty || name2.text!.isEmpty || name3.text!.isEmpty || name4.text!.isEmpty {
        button.isEnabled = false
    } else {
        button.isEnabled = true
    }
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Emm
  • 1,963
  • 2
  • 20
  • 51
0

The textField:shouldChangeCharactersInRange: method gets called BEFORE the contents of the text field have changed.

You should special-case the current text field and figure out if it's new contents are going to be blank after the edit.

Something like this:

textField.text.length > range.length - replacementString.length

(That's off the top of my head, and not debugged. Plus you would have to come up with logic that makes sure all the other fields are non-blank, and use the logic above to test the contents of the current field.)

Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

Take a look at UITextFieldValidator. I have created a validator which covers almost each validation required in an app. Also its in swift. So go ahead and check it out.

iAnurag
  • 9,286
  • 3
  • 31
  • 48
  • 8
    Note that [link-only answers](http://meta.stackoverflow.com/tags/link-only-answers/info) are discouraged, SO answers should be the end-point of a search for a solution (vs. yet another stopover of references, which tend to get stale over time). Please consider adding a stand-alone synopsis here, keeping the link as a reference – kleopatra Sep 01 '15 at 08:15
  • @iAnurag , very nice, but the code will still give error if textfield starts with number but contains alphabets also. so what should be the solution of this??? – ArgaPK Dec 14 '17 at 12:45
0

Try this code. Hope you will get what to do.

func updateSaveButtonState() {
    let text = titleTextField.text ?? ""
    saveButton.isEnabled = !text.isEmpty
}


@IBAction func textEditingChanged(_ sender: UITextField) {
    updateSaveButtonState()
}

@IBAction func returnPressed(_ sender: UITextField) {
    titleTextField.resignFirstResponder()
}
Vitalik Kizlov
  • 385
  • 5
  • 9