I am trying to figure this out. When I use the code below, I am able to move from one text field to the next and click the "Backspace" button but ONLY when the text field has text in there.
My Question: How can I click the "Backspace" button when the text field is blank and move to the previous text field?
Second Question: How do I get rid of the blinking blue line on the UITextField
?
Below is the code that I have. Any help will be greatly appreciated!
@objc func textFieldDidChange(textfield: UITextField) {
let text = textfield.text!
if text.utf16.count == 0 {
switch textfield {
case textField2:
textField1.becomeFirstResponder()
textField1.backgroundColor = UIColor.black
case textField3:
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.black
case textField4:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.black
case textField5:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.black
case textField6:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.black
textField6.resignFirstResponder()
textField6.backgroundColor = UIColor.black
default:
break
}
}
else if text.utf16.count == 1 {
switch textfield {
case textField1:
textField1.backgroundColor = UIColor.black
textField1.textColor = .white
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.black
textField2.textColor = .white
case textField2:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.black
textField3.textColor = .white
case textField3:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.black
textField4.textColor = .white
case textField4:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.black
textField5.textColor = .white
case textField5:
textField6.becomeFirstResponder()
textField6.backgroundColor = UIColor.black
textField6.textColor = .white
case textField6:
textField6.resignFirstResponder()
default:
break
}
}
}
Regarding the blinking blue line, use tintColor:
textfield.tintColor = UIColor.black
You can use the textfield's background color to hide it.
You should subclass UITextField
to implement the deleteBackward()
function. Also, you should implement a protocol which has a function that executes when deleteBackward()
is called and the text is empty.
DISCLAIMER: This code's origin is in the VENTokenField project. It was converted to Swift and tweaked by me.
class BackspaceTextField: UITextField {
weak var backspaceTextFieldDelegate: BackspaceTextFieldDelegate?
override func deleteBackward() {
if text?.isEmpty ?? false {
backspaceTextFieldDelegate?.textFieldDidEnterBackspace(self)
}
super.deleteBackward()
}
}
protocol BackspaceTextFieldDelegate: class {
func textFieldDidEnterBackspace(_ textField: BackspaceTextField)
}
Instead of handling each text field separately, create an array of text fields in your view controller and handle first responders there. If there is a previous view controller, this sets it as the first responder (you don't need to resign anything, as the previous responder will automatically resign after the change). If it's the first text field in the array, it ends editing.
class ViewController: UIViewController, BackspaceTextFieldDelegate {
var textField1: BackspaceTextField!
var textField2: BackspaceTextField!
[...]
var textFields: [BackspaceTextField] {
return [textField1, textField2, [...]]
}
override func viewDidLoad() {
super.viewDidLoad()
textFields.forEach { $0.backspaceTextFieldDelegate = self }
}
func textFieldDidEnterBackspace(_ textField: BackspaceTextField) {
guard let index = textFields.index(of: textField) else {
return
}
if index > 0 {
textFields[index - 1].becomeFirstResponder()
} else {
view.endEditing(true)
}
}
}
Regarding Question 1:
Change the tint color of the text field as @Jon mentioned.
Regarding Question 2:
What I did was create an extension for the TextFieldDelegate. Within that, I created an additional function to alert, using NotificationCenter.default as the function needed to be defined in the present class.
extension UITextFieldDelegate {
func deleteSelected(field: UITextField) {
NotificationCenter.default.post(name: Notification.Name("deleteSelected"), object: field)
}
}
Note, that in order for this to work correctly, you must call the function when the delete key was pressed. This can be done in your subclass of UITextField, by providing:
override func deleteBackward() {
super.deleteBackward()
delegate?.deleteSelected(field: self)
}
Finally, you need to listen for the event by adding an observer to that notification in the view you want to take action on.
NotificationCenter.default.addObserver(self, selector: #selector(checkForDelete(_ :)), name: Notification.Name("deleteSelected"), object: nil)
If you have not defined checkForDelete(_ :), this is what mine looks like:
@objc func checkForDelete(_ notification: Notification) {
if let field = notification.object as? MyTextField {
if let textFound = field.text, !textFound.isEmpty {
// Do nothing extra
print("Text found: \(textFound).")
} else {
// Go to previous field
guard let activeField = active(textField: field).field, let index = active(textField: field).index else {
print("Field could not be determined")
return
}
print("Going backwards")
activeField.text = nil
activeField.resignFirstResponder()
activeField.stateOfTextField = .empty
textFields[index == 0 ? 0 : index - 1].becomeFirstResponder()
textFields[index == 0 ? 0 : index - 1].text = nil
}
}
}
The 'Guard' in the else statement determines what the currently active text field is, and returns the index (they are stored in an array) so we can go to the previous TextField, if there is one.