简体   繁体   中英

Can I detect the delete key even if the UITextField is empty?

When UITextField contains nothing, pressing the delete key of keyboard won't call any of UITextFieldDelegate's methods.

How can I detect it?

EDIT: There seems no trivial way to do it. The most useful links I can find are:

  1. UITextField : Any way to detect the Delete key event when the field is empty ?

  2. How to get the actual key pressed in a UITextField

In short, my solution is to put a permanent SPACE at the start of the text field. And make other nesessary changes(textFieldShouldReturn:, textField:shouldChangeCharactersInRange:replacementString:, etc.).

This seems doable by subclassing UITextField. UITextField conforms to a protocol called UITextInput which inturn conforms to another protocol called UIKeyInput. UIKeyInput protocol has a method deleteBackward which fires every time the backspace key in keyboard is pressed.

Here is how it looks

Header

#import <UIKit/UIKit.h>

@interface EmptyDeleteTextField : UITextField

@end

Implementation

- (void)deleteBackward
{
    NSLog_SM(@"Length: %d", (int)self.text.length);
    [super deleteBackward];
}

PS: Don't forget to call super because you don't want to mess with the default behaviour of UITextField.

I'm adding this answer for a more complete example in swift 3. Basically, I needed a pincode type view where I have multiple text fields that allow one character in each cell.

like this 在此处输入图片说明 I started by creating a subclass of UITextField and a protocol that defines a new func.

protocol MYDeleteActionTextFieldDelegate {
    func textFieldDidSelectDeleteButton(_ textField: UITextField) -> Void
}
class MYDeleteActionTextField: UITextField {
    var deleteDelegate: MYDeleteActionTextFieldDelegate?
    override func deleteBackward() {
        // Need to call this before super or the textfield edit may already be in place
        self.deleteDelegate?.textFieldDidSelectDeleteButton(self)
        super.deleteBackward()
    }
}

Then you create the text fields with the new subclass and implement the delegate in your view controller. In my case, I manage the textfields in an array for ease of use and layout the cells with PureLayout. I store them like this

var pinFields = UITextField

Then in viewDidLoad() , I add all the pin fields into the array like so:

for _ in 1...6 {
            let field = EDFDeleteActionTextField.init(forAutoLayout: ())
            field.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: UIControlEvents.editingChanged)
            field.delegate = self
            field.deleteDelegate = self
            field.textAlignment = .center
            field.font = UIFont.newBigTitle()
            field.textColor = UIColor.edfBlue()
            field.backgroundColor = UIColor.edfWhite()
            self.pinFields.append(field)
            self.pinView.addSubview(field)
        }

Now you just need to respond to all the appropriate delegate methods and the textFieldDidChange target that was added above.

// MARK: UITextFieldDelegate
    func textFieldDidChange(textField: UITextField) {
        // If the user typed one character, move to the next cell.
        if (textField.text?.characters.count == 1) {
            let index = pinFields.index(of: textField)
            textField.resignFirstResponder()
            if (pinFields.count > index! + 1) {
                pinFields[index! + 1].becomeFirstResponder()
            }
        } // If they deleted the character move to previous cell
        else if (textField.text?.characters.count == 0) {
            let index = pinFields.index(of: textField)
            if (index! - 1 >= 0) {
                pinFields[index! - 1].becomeFirstResponder()
            }
        }
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if range.location > 0 {
            let index = pinFields.index(of: textField)
            // If there is already text in the text field and the next cell is empty - move the newly typed character to that cell.
            if (pinFields.count > index! + 1) {
                let nextField = pinFields[index! + 1]
                if (nextField.text?.characters.count == 0) {
                    textField.resignFirstResponder()
                    nextField.becomeFirstResponder()
                    nextField.text = string
                }
            }
            return false
        }
        return true
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return false
    }

    // MARK: EDFDeleteActionTextFieldDelegate
    func textFieldDidSelectDeleteButton(_ textField: UITextField) {
        // If user clicked delete, and there are no characters, move to previous cell if available.
        // If there are characters, it is handled in UITextFieldDelegate
        if (textField.text?.characters.count == 0) {
            let index = pinFields.index(of: textField)
            if (index! - 1 >= 0) {
                pinFields[index! - 1].becomeFirstResponder()
            }
            else {
                textField.resignFirstResponder()
            }
        }
    }

I'll leave out the boring parts (like laying out text fields etc), since this general functionality is useful in more cases than this pincode view, but implementing this child class and protocol should give all the functionality that you would need for similar type views and solve for the question at hand (which is probably needing something similar).

Happy coding.

Ok, I've figured it out. so in the - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text delegate method (ie when the text is about to be input) you simply do a

if (text.length <= 0) {
    // backspace
    return FALSE;
} else {

because for backspace text.length will == 0, and everything else it will == 1.

This can be implemented by subclassing UITextField and adding it as custom class of desired textfield. Then override the method "deleteBackward" This method will catch all the backspace events.

Code in Swift:

override func deleteBackward() {
        super.deleteBackward()
        // Enter your stuff here
    }

Also make sure you that are calling super method too, as it is doing the basic functionalities of the event.

在其上方放置一个不可见的按钮。

Its possible to put a button over it, but that's a bit fragile since keyboard layout could change with different iOS versions and there certainly are different keyboard layouts for different languages.

Look at the "Managing Text Fields and Text Views" Apple doc. Im sure there is a way to do this. Its something like a UITextField implements a protocol to get keyevents. One of those key events will probably be a delete key, so you could override whatever method receives these and get it that way.

I am not sure but can you try a custom method using addTarget: action: forControlEvents: . Try the UIControlEventAllEditingEvents option.

For Q1.

In Swift 4.2 below works for me

@objc func keyboardInputShouldDelete(_ textField: UITextField) -> Bool {
        print("User Pressed backspace in empty textfield ")
        return true
    }

When using keypad, obviously you are writing it to some variable (eg: UITextField, UILabel or even a global String)

An option to detect the delete action can be:

  • Keep a global "int previousLengthOfText"
  • Init this var to be lastLengthOfText = 0;
  • catch the change on the delegate function: eg :

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

  • compare the length of your label\\textField to this global var and see if a character was added or deleted.

eg :

if ([self.myTextField.text length] > previousLengthOfText) {
{
// user deleted a character - Do your action here
previousLengthOfText = [self.myTextField.text length];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM