简体   繁体   English

即使UITextField为空,我也可以检测到删除键吗?

[英]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. 当UITextField不包含任何内容时,按键盘的Delete键将不会调用UITextFieldDelegate的任何方法。

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 ? UITextField:当字段为空时,有什么方法可以检测Delete键事件?

  2. How to get the actual key pressed in a UITextField 如何获得在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.). 并进行其他必要的更改(textFieldShouldReturn:,textField:shouldChangeCharactersInRange:replacementString:等)。

This seems doable by subclassing UITextField. 通过子类化UITextField似乎可以做到这一点。 UITextField conforms to a protocol called UITextInput which inturn conforms to another protocol called UIKeyInput. UITextField符合称为UITextInput的协议,而后者又符合另一个称为UIKeyInput的协议。 UIKeyInput protocol has a method deleteBackward which fires every time the backspace key in keyboard is pressed. UIKeyInput协议具有一个deleteBackward方法,该方法在每次按下键盘上的退格键时触发。

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. PS:不要忘了调用super,因为您不想弄乱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. 我将这个答案添加为Swift 3中更完整的示例。基本上,我需要一个pincode类型视图,其中有多个文本字段,每个文本字段允许一个字符。

like this 像这样 在此处输入图片说明 I started by creating a subclass of UITextField and a protocol that defines a new func. 我首先创建UITextField的子类和定义新功能的协议。

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. 就我而言,我管理数组中的文本字段以便于使用和使用PureLayout对单元进行布局。 I store them like this 我这样储存它们

var pinFields = UITextField var pinFields = UITextField

Then in viewDidLoad() , I add all the pin fields into the array like so: 然后在viewDidLoad() ,将所有pin字段添加到数组中,如下所示:

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. 现在,您只需要响应所有适当的委托方法和上面添加的textFieldDidChange目标即可。

// 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). 我将省去一些无聊的部分(例如布置文本字段等),因为这种通用功能比此pincode视图在更多情况下有用,但是实现此子类和协议应该会提供类似类型所需的所有功能查看并解决眼前的问题(可能需要类似的内容)。

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 因此,在- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text委托方法中(即,当文本将要输入时),您只需执行

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

because for backspace text.length will == 0, and everything else it will == 1. 因为对于退格键text.length将== 0,其他则将== 1。

This can be implemented by subclassing UITextField and adding it as custom class of desired textfield. 这可以通过子类化UITextField并将其添加为所需文本字段的自定义类来实现。 Then override the method "deleteBackward" This method will catch all the backspace events. 然后重写方法“ deleteBackward”。该方法将捕获所有退格事件。

Code in Swift: 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. 可以在其上放一个按钮,但这有点脆弱,因为键盘布局可能会随不同的iOS版本而变化,并且对于不同的语言,肯定会有不同的键盘布局。

Look at the "Managing Text Fields and Text Views" Apple doc. 查看“管理文本字段和文本视图” Apple文档。 Im sure there is a way to do this. 我确定有办法做到这一点。 Its something like a UITextField implements a protocol to get keyevents. 它类似于UITextField,它实现了一个协议来获取关键事件。 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: . 我不确定,但是可以使用addTarget: action: forControlEvents:尝试自定义方法。 Try the UIControlEventAllEditingEvents option. 尝试使用UIControlEventAllEditingEvents选项。

For Q1. 对于Q1。

In Swift 4.2 below works for me 在下面的Swift 4.2中对我有用

@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) 当使用键盘时,显然您正在将其写入某个变量(例如:UITextField,UILabel甚至是全局String)

An option to detect the delete action can be: 检测删除操作的选项可以是:

  • Keep a global "int previousLengthOfText" 保持全局“ int previousLengthOfText”
  • Init this var to be lastLengthOfText = 0; 将这个lastLengthOfText = 0;初始化为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. 将label \\ textField的长度与此全局变量进行比较,看看是否添加或删除了字符。

eg : 例如:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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