How can I move to the previous UITextField if the

2019-03-01 02:14发布

问题:

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

        }
    }
}

回答1:

Regarding the blinking blue line, use tintColor:

textfield.tintColor = UIColor.black

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



回答2:

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


回答3:

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.