check all textfields simultaneously & only enable button if they have text - ios

I want to enable and change color of a button depending on text in password and confirm password textfields. So I found this code online:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let tfConfirmPasswordText = (tfConfirmPassword.text! as NSString).replacingCharacters(in: range, with: string)
if tfConfirmPasswordText.isEmpty {
btnContinue.backgroundColor = UIColor(named: "labelColor")
btnContinue.isEnabled = false
}
else if tfPassword.text != "" && !tfConfirmPasswordText.isEmpty {
btnContinue.backgroundColor = UIColor(named: "accentColor")
btnContinue.isEnabled = true
}
return true
}
This code does work and using these conditions it will enable or disable continue button according to confirmPassword textfield.
But the problem is that after filling both password and confirmPassword textfields, if I remove text from password field it still keeps the button enabled and only disables it if text is removed from confirmPassword textfield.
And if I put password.delegate = self alongside confirmPassword.delgate = self in viewDidLoafd(), it crashes when I put more than 1 character in any textfield.
Is there any way to enable or disable button by always keeping a check on both the textfields instead of just one..ie. confirmPassword textfield?

Instead of using shouldChangeCharactersIn ,i used like below to make it works
override func viewDidLoad() {
super.viewDidLoad()
//initially at disabled
disabledButton()
[tfPassword, tfConfirmPasswordText].forEach({ $0.addTarget(self, action: #selector(textFieldEditingDidChange), for: .editingChanged) })
}
#objc func textFieldEditingDidChange(sender: UITextField) {
guard
let tfPasswordTxtValue = tfPassword.text, !tfPasswordTxtValue.isEmpty,
let tfConfirmPasswordTextValue = tfConfirmPasswordText.text, !tfConfirmPasswordTextValue.isEmpty
else {
disabledButton()
return
}
enableButton()
}
func disabledButton(){
btnContinue.backgroundColor = UIColor(named: "labelColor")
btnContinue.isEnabled = false
}
func enableButton(){
btnContinue.isEnabled = true
}

I think you're missing a test:
Instead of
if tfConfirmPasswordText.isEmpty
I would have write
if tfConfirmPasswordText.isEmpty || tfpasswordText.isEmpty

Related

Disable button as long as several textfields are empty

I have the following code to disable a button as long a textfield is empty:
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if !text.isEmpty{
addButton.isEnabled = true
} else {
addButton.isEnabled = false
}
return true
}
It works fine, but now that I have 3 textfields, I want the button only to be enabled, if all textfields are not empty. So far, as soon as one textfield is filled in, the button is being enabled.
How can I adjust my code to do so?
Add target to all textfields for .editingChanged event, and check if any textfield is empty. If all text fields contain text enable the button else disable the button.
class TestViewController: UIViewController, UITextFieldDelegate {
let addButton = UIButton()
let textField1 = UITextField()
let textField2 = UITextField()
let textField3 = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
textField1.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
textField2.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
textField3.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
}
#objc func textChanged(_ textField: UITextField) {
addButton.isEnabled = [textField1, textField2, textField3].contains { $0.text!.isEmpty }
}
}
As your requirement first you have to create outlet for each textfield and you can enable the button as,
#IBAction func textFieldValueChanged(_ sender: Any)
{
if firstTextField.text != "" && secondTextField.text != "" && thirdTextField.text != "" {
addButton.isEnabled = true
} else {
addButton.isEnabled = false
}
return true
And connect each textfield with the above action for valueChanged event
Well, I don't think the accepted answer is an elegant solution to this problem.
I would suggest to add the following observer in your viewDidLoad:
NotificationCenter.default.addObserver(self, selector: #selector(validate), name: UITextField.textDidChangeNotification, object: nil)
Then define the selector:
#objc func validate(){
var filteredArray = [textFieldOne,textFieldTwo,textFieldThree,textFieldFour].filter { $0?.text == "" }
if !filteredArray.isEmpty {
button.isHidden = true
} else {
button.isHidden = false
}
}

How to handle multiple text fields effectively in UITextField's shouldChangeCharactersIn

I've a number of textFields, I want to change text colour to white when user is typing them in text field. Following is my code with a lot of if conditions which doesn't seem to be efficient. Is there any way to do it without writing a lot of if conditions?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == emailTextField {
emailTextField.textColor = .white
} else if textField == nameTextField {
nameTextField.textColor = .white
} else if textField == addressTextField {
addressTextField.textColor = .white
}
return true
}
Just do
textField.textColor = .white
and whatever the textfield is it's textColor will be changed

Prevent UITextField from taking values on changing the firstResponder

I have four UITextFields, each of which represents a single digit of an OTP. I want the control to shift to consecutive textfield as the user types in the code. My implementation is below.
extension FillSignUpCodeController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = ""
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let inputString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
if inputString.count == 1 {
switch textField {
case textFieldCodeFirstDigit:
textFieldCodeFirstDigit.text = inputString
textFieldCodeFirstDigit.resignFirstResponder()
textFieldCodeSecondDigit.becomeFirstResponder()
case textFieldCodeSecondDigit:
textFieldCodeSecondDigit.text = inputString
textFieldCodeSecondDigit.resignFirstResponder()
textFieldCodeThirdDigit.becomeFirstResponder()
case textFieldCodeThirdDigit:
textFieldCodeThirdDigit.text = inputString
textFieldCodeThirdDigit.resignFirstResponder()
textFieldCodeFourthDigit.becomeFirstResponder()
case textFieldCodeFourthDigit:
textFieldCodeFourthDigit.text = inputString
textFieldCodeFourthDigit.resignFirstResponder()
default:
return false
}
}
return true
}
}
With this piece of code, as the user types the first digit, the first textfield takes the input value and moves the control to the next textfield. However, the second text field is taking the value of the first digit. I tried setting the text to empty after changing the firstResponder but it did not work. How can I fix this issue? Thanks.
Since textField(_:shouldChangeCharactersIn:replacementString:) executes before the text is present in the text field, you should put the code inside a method that is called when the text did already change.
You should observe the changes of your text field and execute the responder-changing code there. You can find more information about that in this question.
Moreover, resigning the first responder before changing it is redundant, you don't need to do that.
It is also very redundant to handle every text field separately. I'd recommend including your text fields in an array and iterating through them.
shouldChangeCharactersInRange gets called before the textField is filled. You can do it by:
textField.addTarget(self, action: #selector(textFieldChangedValue(textField:)), for: UIControlEvents.editingChanged)
write above line for all textfields in viewDidLoad()
#objc func textFieldChangedValue(textField: UITextField) {
print(textField.text)
}
This will work
Following the answers from #the4kman and #Rahul Dasgupta, I have implemented the following:
FillUpCodeViewController.swift
override func viewDidLoad() {
setupArrrayofTextFields(textField1: textFieldCodeFirstDigit,
textField2: textFieldCodeSecondDigit,
textField3: textFieldCodeThirdDigit,
textField4: textFieldCodeFourthDigit)
}
func setupArrrayofTextFields(textField1: UITextField, textField2: UITextField,
textField3: UITextField, textField4: UITextField) {
arrayOftextFields = [textField1, textField2, textField3, textField4]
for textField in arrayOftextFields {
textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)
}
}
#objc func textFieldDidChange(textField: UITextField) {
guard textField.text?.count == 0 else {
let index: Int = arrayOftextFields.index(of: textField)!
guard index == (arrayOftextFields.count-1) else {
arrayOftextFields[index+1].becomeFirstResponder()
return
}
textField.resignFirstResponder()
return
}
}
And, again in the viewcontroller in which I have to implement the submission of recovery code, I simply inherited the FillUpCodeViewController class.
RecoveryViewController.swift
override func viewDidLoad() {
setupArrrayofTextFields(textField1: textFieldRecoveyCodeFirstDigit,
textField2: textFieldRecoveyCodeSecondDigit,
textField3: textFieldRecoveyCodeThirdDigit,
textField4: textFieldRecoveyCodeFourthDigit)
}

Change UITextField responder on Max Length

I have login controller where it has two textFields:
Access Card
Password
The Max Length for the access card is 9 and once the user type the ninth number, it should appear on the access card filed then the cursor needs to move to the password field.
In my code, the cursor is moving when the user clicks to enter the ninth number but the number doesn't appear and the cursor moves to the password field.
For example: I want to enter "123456789" as access card. Once I click "9" it doesn't appear but the cursor moves to password field:
LoginController.swift:
let ACCESSCARD_MAXLENGTH = 9
let PASSWORD_MAXLENGTH = 12
var AccessCardtextFieldLength = 0
var PasswordTextFieldLength = 0
class LoginViewController: UIViewController , UITextFieldDelegate {
#IBOutlet weak var AccessCardTextField: UITextField!
#IBOutlet weak var PasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// calling the function that initialize textFields
initializeTextFields()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// function is used to initialize textFields
func initializeTextFields () {
// To set the focus on the access card once the view load.
AccessCardTextField.becomeFirstResponder()
// This must be defined so we can apply the text field functions on it
AccessCardTextField.delegate = self
PasswordTextField.delegate = self
// Define the keyboard type of the textFields.
AccessCardTextField.keyboardType = UIKeyboardType.NumberPad
PasswordTextField.keyboardType = UIKeyboardType.ASCIICapable
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
AccessCardtextFieldLength = (textField.text?.characters.count)! + string.characters.count
PasswordTextFieldLength = (textField.text?.characters.count)! + string.characters.count
if (textField == AccessCardTextField){
for i in 0..<ACCESSCARD_MAXLENGTH{
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
PasswordTextField.becomeFirstResponder()
}
else{
return true
}
return false
}
}
if (textField == PasswordTextField){
return PasswordTextFieldLength <= PASSWORD_MAXLENGTH ? true : false
}
return true
}
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool will only update when it return true. In this case you are changing the firstResponder therefore it is not updated.
My suggestion is to use add target for this case. This is what you can do:
override func viewDidLoad() {
super.viewDidLoad()
// calling the function that initialize textFields
initializeTextFields()
accessCardTextField.addTarget(self, action: #selector(LoginViewController.accessCardTextFieldChanged(_:)), forControlEvents: .EditingChanged)
}
func accessCardTextFieldChanged(textField: UITextField) {
if textField.text?.characters.count == ACCESSCARD_MAXLENGTH {
modelTextField.becomeFirstResponder()
}
}
This way, it save you quite a few line of code. Most importantly, only accessCardTextField changed will be call. You could do another function to check your password textfield length separately. Also, i renamed from AccessCardTextField to accessCardTextField. It is recommended to have variable starting with lower case.
Not very sure if this works, but try edit your if statement contents with this.
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
if (textField == AccessCardTextField) {
textField.resignFirstResponder()
PasswordTextField.becomeFirstResponder()
}
}
In this condition
if (AccessCardtextFieldLength == ACCESSCARD_MAXLENGTH){
PasswordTextField.becomeFirstResponder()
**return true**
}
else{
return true
}
you returning flase that's why it doesn't show your last Character.

UITextField not updating when entering the keyboard keys

When I tap on this UITextFieldmentioned on screen below, a numpad keyboard appears, the problem is that when I start tap on number, my UITextField do not update, stay with no number, only placeholder.
What I need to do?
Thanks
ADDING CODE!
#IBOutlet weak var salarioTextField: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
updateView()
salarioTextField.delegate = self
}
func textFieldShouldBeginEditing(textField: UITextField) -> Bool
{
if textField == feriasTextField
{
feriasTextField.inputView = feriasPicker
}
else if textField == salarioTextField
{
salarioTextField.keyboardType = UIKeyboardType.NumberPad
}
else if textField == inicioTextField
{
textField.inputView = datePicker
textField.inputAccessoryView = toolbar
datePicker.date = inicioDate
}
else if textField == motivoTextField
{
motivoTextField.inputView = motivoPicker
}
else
{
textField.inputView = datePicker
textField.inputAccessoryView = toolbar
datePicker.date = fimDate
}
return true
}
func textFieldDidBeginEditing(textField: UITextField)
{
backgroundScrollView.scrollEnabled = true
let scrollSize = CGSizeMake(view.frame.width, view.frame.height)
backgroundScrollView.contentSize = scrollSize
activeTextField = textField
activeTextField?.becomeFirstResponder()
}
func textFieldDidEndEditing(textField: UITextField)
{
backgroundScrollView.contentSize = CGSizeMake(0, 800)
backgroundScrollView.scrollEnabled = true
}
Please ignore the others UITextField.
Make sure you have enabled user interaction for the field.
If you implement the delegate method textViewDidBeginEditing, you can put a breakpoint or a print statement here to see if it triggers.
You can try calling becomeFirstResponder on the field.
I just figured out what was happening.
I was returning false to the method below.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
return true;
}
Just changed it to true and it's working.
Thanks for your help.

Resources