Box House
Box House

Reputation: 177

How can I move to the previous UITextField if the "Backspace" button is pressed and the text field is blank and in Swift 4?

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

        }
    }
}

Upvotes: 3

Views: 2239

Answers (4)

RAHULSAGAR KULKARNI
RAHULSAGAR KULKARNI

Reputation: 1

When you have four Textfields inputOne, inputTwo, inputThree, inputFour create 4 @IBOutlet like @IBOutlet weak var inputOne, @IBOutlet weak var inputOne, @IBOutlet weak var inputThree, @IBOutlet weak var inputFour

This is very simple by adding white space on beginning of TextField editing

if user enters the value like "1" to "9" next text field becomes first responder If user taps on backButton if textfield contains value replace that with white space and if user taps on backButton if textfield contains white space the previous textfield becomes first responder

Only i m using two TextField delegate Methods

  1. textFieldDidBeginEditing
  2. shouldChangeCharactersIn

func textFieldDidBeginEditing(_ textField: UITextField) {

    if(textField.text == ""){
        textField.text = " "
    } 

}

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

    let maxLength = 1
    let currentString: NSString = textField.text! as NSString
    let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
    if let char = string.cString(using: String.Encoding.utf8) {
        let isBackSpace = strcmp(char, "\\b")
        if (isBackSpace == -92 && textField.text == " ") {
            if(textField.tag == 0){
                self.focusOnTextField(selTextField:inputOne)
         
                return false
            }else if(textField.tag == 1){
                self.focusOnTextField(selTextField:inputOne)
                
                return false
            }else if(textField.tag == 2){
                self.focusOnTextField(selTextField:inputTwo)
                
                return false
            }else if(textField.tag == 3){
                self.focusOnTextField(selTextField:inputThree)
                
                return false
            }
        }
        else if (isBackSpace == -92 && textField.text != " ") {
            textField.text = " "
            
            return false
        }
        else {
            textField.text = textField.text?.replacingOccurrences(of:textField.text!, with:string)
        }
    }
    
    let newPosition = textField.endOfDocument
    textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
    if(textField.tag == 0){
        self.focusOnTextField(selTextField:inputTwo)
    } else if(textField.tag == 1){
        self.focusOnTextField(selTextField:inputThree)
    }else if(textField.tag == 2){
        self.focusOnTextField(selTextField:inputFour)
    }
    return newString.length <= maxLength
}
 
func focusOnTextField(selTextField:UITextField) {       
        selTextField.becomeFirstResponder()
}

Upvotes: 0

Jstngoulet
Jstngoulet

Reputation: 1184

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.

Upvotes: 0

glyvox
glyvox

Reputation: 58029

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

Upvotes: 4

Jon
Jon

Reputation: 26

Regarding the blinking blue line, use tintColor:

textfield.tintColor = UIColor.black

You can use the textfield's background color to hide it.

Upvotes: 1

Related Questions