Swift - validating UITextField - swift

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?

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
}
}

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
}

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.)

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.

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()
}

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
}
}

Related

How to change textField user-interaction according to condition in Swift

I have two textfields called toDateTextField and fromDateTextField
my need is if fromDateTextField.text?.isEmpty ?? true then if i tap on toDateTextField then it has to show toast and should not open datepicker
with this code if i tap initially on toDateTextField then datepicker is not opening but it is not showing toast.. how to show toast initially if i tap on toDateTextField
and if i initially tap on fromDateTextField then its showing toast message and then datepicker is coming.. how to solve this two issues
override func viewDidLoad() {
super.viewDidLoad()
toDateTextField.isEnabled = false
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if fromDateTextField.text?.isEmpty ?? true {
toDateTextField.isEnabled = false
self.view.makeToast("Please select from date")
} else {
toDateTextField.isEnabled = true
}
}
//this is datpicker done button
#objc func doneButtonPressed(_ sender: UIBarButtonItem) {
toDateTextField.isEnabled = true
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let arr = [fromDateTextField,toDateTextField]
let current = arr[sender.tag]
if let datePicker = current?.inputView as? UIDatePicker {
current?.text = dateFormatter.string(from: datePicker.date)
current?.resignFirstResponder()
}
}
how to solve this two issues.. please do help
Nothing will happen if you initially tap on toDateTextField because you have set its enabled property to false in viewDidLoad method.
And you will get the toast message as soon as you start modifying fromDateTextField because it is empty at the beginning.
To solve that, you need to do some modifications in textFieldDidBeginEditing so that it detects which textField is currently being changed and do what you want accordingly.
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == toDateTextField {
if fromDateTextField.text?.isEmpty ?? true {
self.view.makeToast("Please select from date")
toDateTextField.isEnabled = false
}
}
}
And to enable toDateTextField you need to add the following:
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == fromDateTextField {
toDateTextField.isEnabled = true
}
}
Note: You need to set delegate of both textFields to self in your viewDidLoad for this solution to work properly.

Why is the Button enable not working when TextView has contents?

Likely a very simple rookie mistake going on here but I'm trying to make sure a button is disabled until a TextView has text in it.
I set
Button.isEnabled = false
and then added this code. I've tried variations of this code in various ways, I'm seemingly unable to attach it to the textView itself, I can't imagine why this wouldn't be working.
#IBAction func valueChanged(_ sender: AnyObject)
{
if(title.text != "")
{
Button.isEnabled = true
}
else
{
Button.isEnabled = false
}
}
Thanks StackedOverflow Community ...
set button's enabled with alpha to make the look better then set enabled/disabled through valueChanged
#IBOulet private weak var myButton: UIButton! {
didSet {
set(myButton, enabled: false)
}
}
#IBAction private func valueChanged(_ sender: UITextView) {
set(myButton, enabled: !sender.text.isEmpty)
}
private func set(_ button: UIButton, enabled: Bool) {
button.isEnabled = enabled
button.alpha = enabled ? 1 : 0.5
}

Why does UITextFIeld return optional value in Swift?

Im using a UITextField to enter numbers but when I enter the first number it ends up printing nil. Then when I enter other numbers it prints out the number I inputed but with an optional return type. Why does that happen?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//convert string into number
let input = (numberLabel.text! as NSString).integerValue
//find answer to label when added by 1111
let answer = input + 1111
print(answer)
let numberText = Int(numberField.text!)
print("This is number text: \(numberText)
if answer == numberText {
print("correct")
//if user gets answer correct
} else {
//if user gets answer incorrect
}
}
The reason is probably because you're using shouldChangeCharactersIn which doesn't indicate the change in the character until the second character. Ideally you want the user to be notified of the correct answer after they complete the answer and submit it, in which case you want to use something like textFieldDidEndEditing:
class MyVC: UIViewController, UITextFieldDelegate {
let textField = UITextField()
var numberLabel: UILabel {
let label = UILabel()
label.text = "100"
return label
}
let button = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(textField)
textField.borderStyle = .roundedRect
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
textField.widthAnchor.constraint(equalToConstant: 200),
textField.heightAnchor.constraint(equalToConstant: 100)
])
textField.delegate = self
button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside)
button.tag = 1
button.backgroundColor = .black
button.setTitle("Answer", for: .normal)
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: textField.trailingAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 100)
])
}
#objc func buttonPressed(_ sender: UIButton!) {
if case let tag = sender.tag, tag == 1 {
textField.resignFirstResponder()
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
//convert string into number
let input = (numberLabel.text! as NSString).integerValue
//find answer to label when added by 1111
let answer = input + 1111
print(answer)
if let numberText = Int(textField.text ?? "0") {
print("This is number text: \(numberText)")
if answer == numberText {
//if user gets answer correct
print("correct")
} else {
//if user gets answer incorrect
print("wrong")
}
}
}
}
UITextFied text property will never return nil. Its default value is an empty string. What is returning nil is the Int string initializer. Regarding text property optionality IMO is intentional to allow you to assign an optional string without the need to unwrap it first.
let numberText = Int(textField.text!) ?? .zero

Hello, I am getting two errors for textFieldDidChange ( Swift)

Can someone please help me out? I am getting these two errors
use of unresolved identifier
and
use of local variable textFieldDidChange before its declaration
Here is my code
override func viewDidLoad() {
super.viewDidLoad()
// Setting text field delegates
emailTextField.delegate = self
passwordTextField.delegate = self
// Disable login button until both textFields are not nil
textField.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
}
func textFieldDidChange() {
if (emailTextField.text?.isEmpty)! || (passwordTextField.text?.isEmpty)! {
print("text is empty")
loginButton.isEnabled = false
loginButton.alpha = 0.33
} else {
loginButton.isEnabled = true
loginButton.alpha = 0.55
}
}
Edit
Question has been answered however I noticed the button would enable after I typed into the first textField. So I changed the && to || because I wanted the button to be disabled until both textFields weren't empty. Just for anyone who has this issue as well.
Your textFieldDidChange method should be outside the viewDidLoad and should be prefix-ed with #objc since it is a selector.
class YourClass: UIViewController {
// Your code
override func viewDidLoad() {
super.viewDidLoad()
// Setting text field delegates
emailTextField.delegate = self
passwordTextField.delegate = self
// Disable login button until both textFields are not nil
emailTextField.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
passwordTextField.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
}
#objc func textFieldDidChange() {
if (emailTextField.text?.isEmpty)! && (passwordTextField.text?.isEmpty)! {
print("text is empty")
loginButton.isEnabled = false
loginButton.alpha = 0.33
} else {
loginButton.isEnabled = true
loginButton.alpha = 0.55
}
}
}
And the target should be added to the respective text fields - emailTextField and passwordTextField.
You have missed to place "}" for viewDidLoad method
override func viewDidLoad() {
super.viewDidLoad()
// Setting text field delegates
emailTextField.delegate = self
passwordTextField.delegate = self
// Disable login button until both textFields are not nil
textField.addTarget(self, action: #selector(textFieldDidChange), for: UIControlEvents.editingChanged)
}
#objc func textFieldDidChange() {
if (emailTextField.text?.isEmpty)! && (passwordTextField.text?.isEmpty)! {
print("text is empty")
loginButton.isEnabled = false
loginButton.alpha = 0.33
} else {
loginButton.isEnabled = true
loginButton.alpha = 0.55
}
}

Disabling button when textfield is empty wait for any change in other textfield

I wanna disable log in button if email or password textfield is empty and enable it again when both are filled so i use the below code:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if txtEmail.text != "" , txtPassword.text != "" {
btnLogInOutlet.isEnabled = true
}else if txtEmail.text! == "" || txtPassword.text! == "" {
btnLogInOutlet.isEnabled = false
}
return true
}
The problem appears when i type in both fields then i delete what i type in one of those fields . As you can see in the picture below , the button still enabled.It only become disabled again if i start editing in the other (not empty) textfield.
My question is How to disable the button again directly after i delete what i type in any textfield before i move to another one??
You are getting the text value from your textfield before returning true from your delegate - it means that it has old value, before the change.
Instead of using shouldChangeCharactersIn delegate method, use the action (you can also hook it up using storyboards):
txtEmail.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
txtPassword.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
Now in textFieldDidChange(textField: UITextField) method you can copy paste your implementation (slightly improved here):
#objc func textFieldDidChange(textField: UITextField) {
btnLogInOutlet.isEnabled = !txtEmail.text.isEmpty && !txtPassword.text.isEmpty
}
try this ...initially disable your login button..
override func viewDidLoad() {
super.viewDidLoad()
loginbttn.isEnabled = false;
textfield1.addTarget(self, action: #selector(textFieldDidChange(_:)), for:.editingChanged )
textfield2.addTarget(self, action: #selector(textFieldDidChange(_:)), for:.editingChanged )
}
#objc func textFieldDidChange(_ sender: UITextField) {
if textfield1.text == "" || textfield2.text == "" {
loginbttn.isEnabled = false;
}else{
loginbttn.isEnabled = true;
}
}
Happy coding :)
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if txtEmail.text != "" && txtPassword.text != "" {
btnlogin.isEnabled = true
}else if txtEmail.text! == "" || txtPassword.text! == "" {
btnlogin.isEnabled = false
}
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if txtEmail.text != "" && txtPassword.text != "" {
btnlogin.isEnabled = true
}else if txtEmail.text! == "" || txtPassword.text! == "" {
btnlogin.isEnabled = false
}
return true
}
you just have to put the same code in textFieldShouldEndEditing as well and it will work :)