'iOS keyboard: How can I start with a disabled done/return/go button on the keyboard and KEEP it disabled until the validation requirements are met?
I've got a text field delegate setup which disables/enables the done button on keyboard when certain conditions are met (between 5 and 15 characters, not just whitespace, etc...) using the "inputDelegate.returnKeyEnabled" key (which is wrapped by ElId.Key.enableDone
). Everything seemed to work fine, except the done button would start off enabled before editing.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text else { return true }
let newLength = text.count + string.count - range.length
let booleanValue = isValidText(text, length: newLength)
self.rightActionButton?.isEnabled = booleanValue
textField.enablesReturnKeyAutomatically = false
textField.setValue(booleanValue,
forKeyPath: ElId.Key.enableDone)
return true
}
func isValidText(_ text: String, length: Int) -> Bool {
return ((length >= minNameLength) && (length <= maxNameLength)) && !text.trimmingCharacters(in: .whitespaces).isEmpty && !text.last!.isWhitespace && !text.first!.isWhitespace
}
At first I tried to set the textfield's "inputDelegate.returnKeyEnabled" false beforehand, but that didn't work.
After reading other answers here, it looked like I could start with the done button by setting enablesReturnKeyAutomatically
to true before editing. And it sort of did; except: The done button enables after I start typing, until I've tapped a certain number of characters and then start deleting. At this point, it starts to work as expected.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
textField.enablesReturnKeyAutomatically = true
// textField.setValue(false,
// forKeyPath: ElId.Key.enableDone)
return true
}
I've confirmed through breakpoints that enablesReturnKeyAutomatically
is only true at first and then is false after the first keystroke and that the isValidText
method is returning false from the first keystroke; but done button enables as soon as I start typing and then stays enabled until after several keystrokes.
How can I start with a disabled done/return/go button on the keyboard and KEEP it disabled until the validation requirements are met?
UPDATE
Realized that while the length was validating properly, my whitespace checks were still being applied to the previous value not the new one. Thought this might be the culprit and updated my method to correct for this, however now the validation not working at all. isValidText
method still returns false when it should, but done button is always enabled. Maybe this will draw light onto the mistake.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let text = textField.text,
let textRange = Range(range, in: text) else { return true }
let updatedText = text.replacingCharacters(in: textRange,
with: string)
// let newLength = text.count + string.count - range.length
let booleanValue = isValidText(updatedText)//, length: newLength)
self.rightActionButton?.isEnabled = booleanValue
textField.enablesReturnKeyAutomatically = false
textField.setValue(booleanValue,
forKeyPath: ElId.Key.enableDone)
return true
}
func isValidText(_ text: String/*, length: Int*/) -> Bool {
return ((text.count >= minNameLength) && (text.count <= maxNameLength)) && !text.trimmingCharacters(in: .whitespaces).isEmpty && !text.last!.isWhitespace && !text.first!.isWhitespace
}
When I stop setting enablesReturnKeyAutomatically
to true beforehand, the new version of the method works as expected. The only problem is that the Done button is then enabled from the beginning but immediately disables as text is written.
Solution 1:[1]
Actually disabling the return key seems to be difficult. Source: This discussion How to disable/enable the return key in a UITextField?
But, since you're using the 'text selection change' method instead of the 'should return' method, I would guess that maybe a solution that just prevents the user submitting invalid input would work for you?
If so, here is a workaround. It doesn't actually disable the return key, but it does disable the 'NEXT' button under the exact conditions you described in your question. The button starts out disabled, and after every key press, checks again whether to enable/disable the button based on your isValidText
function.
Here is a minimal example so you can test whether this works for you. In order to recreate the project, all you'll have to do is set up the text field, label, and button in Main.storyboard
, copy and paste this into ViewController
, then connect the @IBOutlet
s and @IBAction
:
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var testTextField: UITextField!
@IBOutlet weak var resultLabel: UILabel!
@IBOutlet weak var nextButton: UIButton!
let maxNameLength = 15
let minNameLength = 5
override func viewDidLoad() {
super.viewDidLoad()
testTextField.delegate = self
// Start off the button as disabled, as the button should only be
// enabled under the conditions that valid text has been entered
nextButton.isEnabled = false
}
@IBAction func doNextStep(_ sender: UIButton) {
print("On to Next Step")
}
}
extension ViewController: UITextFieldDelegate {
func isValidText(_ text: String/*, length: Int*/) -> Bool {
return ((text.count >= minNameLength) && (text.count <= maxNameLength)) && !text.trimmingCharacters(in: .whitespaces).isEmpty && !text.last!.isWhitespace && !text.first!.isWhitespace
}
func textFieldDidChangeSelection(_ textField: UITextField) {
if let txt = textField.text {
let result = isValidText(txt)
resultLabel.text = result ? "Valid Text" : "Not Valid"
nextButton.isEnabled = result
} else {
nextButton.isEnabled = false
}
}
}
It isn't actually necessary to use the shouldChangeCharactersInRange
method: you can get the same effect with textFieldDidChangeSelection
since you're checking whether the text is valid in order to proceed/not proceed with some other action, and not to decide whether to allow the text inside the text field to be changed (unless I misunderstood your question).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Quack E. Duck |